From 9df6cf1cb0287d4dc2883bf3bca8ac46eaf8c29f Mon Sep 17 00:00:00 2001 From: Gperez88 Date: Wed, 28 Jan 2026 23:10:59 -0400 Subject: [PATCH 1/6] docs: enhance scene documentation with configurable limits Update the Scene API reference to clarify the default limits for MAX_ENTITIES and MAX_LAYERS, including options for overriding these values via compiler flags. Add warnings regarding ESP32 platform constraints and provide examples for setting custom limits in project configuration. Enhance the manual on platforms and drivers to include similar information for scene limits. --- docs/api_reference/core/scene.md | 37 +++++++++++++------ .../optimization/platforms_and_drivers.md | 16 ++++++++ 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/docs/api_reference/core/scene.md b/docs/api_reference/core/scene.md index 15f8c88..4cf7d84 100644 --- a/docs/api_reference/core/scene.md +++ b/docs/api_reference/core/scene.md @@ -6,7 +6,7 @@ Represents a game level or screen containing entities. A `Scene` manages a collection of Entities and a CollisionSystem. It is responsible for updating and drawing all entities it contains. Scenes provide lifecycle hooks (`init()`, `update()`, `draw()`) to manage gameplay segments. -Scenes are the primary organizational unit in PixelRoot32, similar to levels or screens in other game engines. Each scene can contain up to `MAX_ENTITIES` (32) entities. +Scenes are the primary organizational unit in PixelRoot32, similar to levels or screens in other game engines. Each scene can contain up to `MAX_ENTITIES` (default 32; overridable via compiler flags) entities, and drawing uses up to `MAX_LAYERS` (default 3; overridable) render layers. ## Namespace @@ -154,7 +154,7 @@ Adds an entity to the scene. **Notes:** - Entities are added to an internal queue -- Maximum of `MAX_ENTITIES` (32) entities per scene +- Maximum of `MAX_ENTITIES` (default 32; [overridable](#overriding-scene-limits-max_layers--max_entities)) entities per scene - If the limit is reached, the entity may not be added (check return value if available) - Entities are updated and drawn in the order they were added - The entity's lifetime is managed by the scene (do not delete manually while in scene) @@ -226,7 +226,7 @@ Queue of entities in the scene. Accessible to derived classes for custom entity **Type:** `ArduinoQueue` **Notes:** -- Maximum capacity: `MAX_ENTITIES` (32) +- Maximum capacity: `MAX_ENTITIES` (default 32; [overridable](#overriding-scene-limits-max_layers--max_entities)) - Direct access allows custom iteration or filtering - Use with caution: modifying while iterating may cause issues @@ -241,17 +241,30 @@ System to handle collisions between actors. Accessible to derived classes for cu - Uses collision layers and masks for filtering - Can be accessed for custom collision queries -## MAX_ENTITIES Constant +## Overriding scene limits (MAX_LAYERS / MAX_ENTITIES) -The maximum number of entities allowed per scene. +The engine defines default limits in `core/Scene.h`: **MAX_LAYERS** (default 3) and **MAX_ENTITIES** (default 32). These are guarded with `#ifndef`, so you can override them from your project without modifying the engine. -**Value:** `32` +!!! warning "ESP32 platform limitation" + The default of **3** for `MAX_LAYERS` is due to ESP32 platform constraints (memory and draw-loop cost). On native/PC you can safely use a higher value; on ESP32, increasing it may affect performance or memory. -**Notes:** -- Hard limit: cannot be changed without modifying engine code -- Includes all entity types: actors, UI elements, particles, etc. -- Plan your entity usage carefully -- Use object pooling to reuse entities instead of creating new ones +### Option A: Compiler flags (recommended) + +In your project (e.g. in `platformio.ini`), add the defines to `build_flags` for the environment you use: + +```ini +build_flags = + -DMAX_LAYERS=5 + -DMAX_ENTITIES=64 +``` + +The compiler defines `MAX_LAYERS` and `MAX_ENTITIES` before processing any `.cpp` file. Because `Scene.h` uses `#ifndef MAX_LAYERS` / `#ifndef MAX_ENTITIES`, it will not redefine them and your values will be used. + +**Effect:** +- **MAX_LAYERS**: Number of render layers drawn in `Scene::draw()` (layer 0 = background, 1+ = sprite context). Increasing this allows more distinct draw layers (e.g. background, platforms, gameplay, foreground, UI). +- **MAX_ENTITIES**: On Arduino, the capacity of the scene entity queue when constructed with this value. On native (mock queue), the value is ignored (unbounded). + +See also: [Platforms and Drivers - Scene limits](../../manual/optimization/platforms_and_drivers.md#scene-limits-max_layers--max_entities). ## Usage Example @@ -305,7 +318,7 @@ public: ## Performance Considerations -- **Entity limit**: `MAX_ENTITIES = 32` is a hard limit; plan accordingly +- **Entity limit**: `MAX_ENTITIES` (default 32) can be overridden via [compiler flags](#overriding-scene-limits-max_layers--max_entities); plan accordingly - **Add/Remove**: Frequent add/remove operations can be expensive; use object pooling - **Update order**: Entities are updated in add order; consider order for dependencies - **Collision checks**: CollisionSystem automatically handles actor collisions efficiently diff --git a/docs/manual/optimization/platforms_and_drivers.md b/docs/manual/optimization/platforms_and_drivers.md index ed25ea1..4b09c17 100644 --- a/docs/manual/optimization/platforms_and_drivers.md +++ b/docs/manual/optimization/platforms_and_drivers.md @@ -211,6 +211,22 @@ build_flags = -D PIXELROOT32_ENABLE_SCENE_ARENA # Enable Scene Arena (experimental) ``` +### Scene limits (MAX_LAYERS / MAX_ENTITIES) + +You can override the default scene limits from your project without modifying the engine. The default of **3** for `MAX_LAYERS` is due to **ESP32 platform constraints** (memory and draw-loop cost); on native/PC you can use a higher value. + +**Option A: Compiler flags (recommended)** — in `platformio.ini`, add to `build_flags` for your environment: + +```ini +build_flags = + -DMAX_LAYERS=5 + -DMAX_ENTITIES=64 +``` + +The compiler defines these before any `.cpp` is processed. Because `Scene.h` uses `#ifndef MAX_LAYERS` / `#ifndef MAX_ENTITIES`, your values are used (more render layers drawn in `Scene::draw`, and on Arduino the entity queue capacity when built with `MAX_ENTITIES`). + +See [API Reference - Scene - Overriding scene limits](../../api_reference/core/scene.md#overriding-scene-limits-max_layers--max_entities) for details. + ### Platform Detection ```cpp From ddd1bf5a329c1e3a555a4c4040599fddb368334d Mon Sep 17 00:00:00 2001 From: Gperez88 Date: Thu, 29 Jan 2026 01:58:08 -0400 Subject: [PATCH 2/6] docs: translate and update optimization documentation for Spanish audience Update English documentation sections to Spanish while adding ESP32-specific optimization details. Translate performance tuning guide and clarify sprite data alignment for 16-bit access. Add DMA and IRAM optimization explanations for ESP32 platform. --- docs/api_reference/graphics/renderer.md | 4 +- docs/api_reference/graphics/sprite.md | 4 +- .../manual/optimization/performance_tuning.md | 367 ++---------------- .../optimization/platforms_and_drivers.md | 13 +- 4 files changed, 39 insertions(+), 349 deletions(-) diff --git a/docs/api_reference/graphics/renderer.md b/docs/api_reference/graphics/renderer.md index c90f644..23aff1e 100644 --- a/docs/api_reference/graphics/renderer.md +++ b/docs/api_reference/graphics/renderer.md @@ -537,8 +537,8 @@ void draw(Renderer& renderer) override { - **Integer-only math**: All operations use integer arithmetic for ESP32 efficiency - **Sprite storage**: Store sprite data in flash (const/constexpr) for best performance - **Batch operations**: Group similar draw calls together -- **Tilemaps**: Use tilemaps for backgrounds instead of individual sprites -- **Viewport culling**: Only draw what's visible on screen +- **Tilemaps**: Dibuja un mapa de tiles completo. Implementa viewport culling automático y caché de paleta para máximo rendimiento. +- **Sprites 2bpp/4bpp**: Optimizado para ESP32 (IRAM + acceso de 16 bits). ## ESP32 Considerations diff --git a/docs/api_reference/graphics/sprite.md b/docs/api_reference/graphics/sprite.md index ac8ac4e..9e2ce47 100644 --- a/docs/api_reference/graphics/sprite.md +++ b/docs/api_reference/graphics/sprite.md @@ -145,7 +145,7 @@ static const MultiSprite PLAYER_MULTISPRITE = { ### Members -- `const uint8_t* data`: Packed 2bpp data +- `const uint16_t* data`: Datos empaquetados (4 píxeles por cada 8 bits, alineados a 16 bits) - `const Color* palette`: Local palette (4 colors) - `uint8_t width`: Sprite width - `uint8_t height`: Sprite height @@ -165,7 +165,7 @@ static const MultiSprite PLAYER_MULTISPRITE = { ### Members -- `const uint8_t* data`: Packed 4bpp data +- `const uint16_t* data`: Datos empaquetados (2 píxeles por cada 8 bits, alineados a 16 bits) - `const Color* palette`: Local palette (16 colors) - `uint8_t width`: Sprite width - `uint8_t height`: Sprite height diff --git a/docs/manual/optimization/performance_tuning.md b/docs/manual/optimization/performance_tuning.md index b95155a..3eb2fd6 100644 --- a/docs/manual/optimization/performance_tuning.md +++ b/docs/manual/optimization/performance_tuning.md @@ -19,11 +19,12 @@ This guide covers techniques to improve game performance on ESP32, including ren 4. **Complex calculations**: Expensive math operations 5. **String operations**: String concatenation/formatting -## Rendering Optimization +## Técnicas de Optimización -### Viewport Culling +El motor utiliza varias técnicas para maximizar los FPS, especialmente en hardware limitado como el ESP32. -Only draw entities that are visible on screen: +### 1. Viewport Culling (Recorte de Cámara) +No proceses objetos que están fuera de la pantalla. El motor lo hace automáticamente en `drawTileMap`, pero debes implementarlo en tu lógica de actualización: ```cpp bool isOnScreen(float x, float y, int width, int height, @@ -38,359 +39,39 @@ bool isOnScreen(float x, float y, int width, int height, y + height < cameraY || y > cameraY + screenHeight); } - -void draw(pixelroot32::graphics::Renderer& renderer) override { - camera.apply(renderer); - - // Only draw visible entities - for (auto* entity : entities) { - if (entity->isVisible && - isOnScreen(entity->x, entity->y, entity->width, entity->height, camera)) { - entity->draw(renderer); - } - } -} -``` - -### Reduce Draw Calls - -Batch similar operations: - -```cpp -// ❌ BAD: Many individual draw calls -void drawBackground(Renderer& renderer) { - for (int y = 0; y < 30; y++) { - for (int x = 0; x < 30; x++) { - renderer.drawSprite(tile, x * 8, y * 8, Color::White); - } - } -} - -// ✅ GOOD: Use tilemap (single call) -void drawBackground(Renderer& renderer) { - renderer.drawTileMap(backgroundTileMap, 0, 0, Color::White); -} -``` - -### Optimize Sprite Drawing - -- **Reuse sprites**: Define once, use many times -- **Use 1bpp**: Most efficient format -- **Limit sprite size**: Smaller sprites = faster drawing -- **Avoid flipping**: Flipping has overhead - -### Efficient Render Layers - -```cpp -// Organize by layer to minimize layer switches -void draw(pixelroot32::graphics::Renderer& renderer) override { - // Draw all layer 0 entities - for (auto* entity : layer0Entities) { - if (entity->isVisible) entity->draw(renderer); - } - - // Draw all layer 1 entities - for (auto* entity : layer1Entities) { - if (entity->isVisible) entity->draw(renderer); - } - - // Draw all layer 2 entities - for (auto* entity : layer2Entities) { - if (entity->isVisible) entity->draw(renderer); - } -} -``` - -## Logic Optimization - -### Reduce Calculations Per Frame - -Cache expensive calculations: - -```cpp -class OptimizedActor : public pixelroot32::core::Actor { -private: - float cachedDistance = 0.0f; - bool distanceDirty = true; - -public: - void update(unsigned long deltaTime) override { - // Only recalculate when position changes - if (distanceDirty) { - cachedDistance = sqrt(x * x + y * y); - distanceDirty = false; - } - - // Use cached value - if (cachedDistance > maxDistance) { - // ... - } - } - - void setPosition(float newX, float newY) { - x = newX; - y = newY; - distanceDirty = true; // Mark for recalculation - } -}; -``` - -### Lazy Evaluation - -Only calculate when needed: - -```cpp -class LazyCalculator { -private: - mutable bool cached = false; - mutable float cachedValue = 0.0f; - -public: - float getValue() const { - if (!cached) { - cachedValue = expensiveCalculation(); - cached = true; - } - return cachedValue; - } - - void invalidate() { - cached = false; - } -}; -``` - -### Avoid Expensive Operations - -```cpp -// ❌ BAD: sqrt() every frame -float distance = sqrt(dx * dx + dy * dy); - -// ✅ GOOD: Use squared distance -float distanceSq = dx * dx + dy * dy; -if (distanceSq > maxDistanceSq) { - // ... -} - -// ❌ BAD: sin/cos every frame -float x = cos(angle) * radius; -float y = sin(angle) * radius; - -// ✅ GOOD: Pre-calculate or use lookup table -static const float COS_TABLE[360] = { /* ... */ }; -static const float SIN_TABLE[360] = { /* ... */ }; -float x = COS_TABLE[static_cast(angle) % 360] * radius; -``` - -## Collision Optimization - -### Use Collision Layers Efficiently - -```cpp -// ❌ BAD: Check everything against everything -for (auto* actor1 : actors) { - for (auto* actor2 : actors) { - if (actor1 != actor2) { - checkCollision(actor1, actor2); - } - } -} - -// ✅ GOOD: Use layers to reduce checks -// CollisionSystem automatically uses layers -// Only actors with matching layers/masks are checked -``` - -### Reduce Collision Checks - -```cpp -// Only check collisions for active actors -void update(unsigned long deltaTime) override { - for (auto* actor : actors) { - if (actor->isEnabled && actor->isActive) { - actor->update(deltaTime); - } - } - - // CollisionSystem only checks enabled actors - Scene::update(deltaTime); -} ``` -### Simple Hitboxes +### 2. Optimización de Memoria y CPU (ESP32) -```cpp -// ✅ GOOD: Simple AABB -pixelroot32::core::Rect getHitBox() override { - return {x, y, width, height}; -} +Para la plataforma ESP32, se han implementado optimizaciones de bajo nivel críticas: -// ❌ BAD: Complex shape calculations -pixelroot32::core::Rect getHitBox() override { - // Complex polygon calculations... -} -``` +- **IRAM_ATTR**: Las funciones críticas de renderizado (`drawSprite`, `drawTileMap`, etc.) están marcadas para ejecutarse desde la RAM interna (IRAM), eliminando la latencia de lectura de la Flash SPI. +- **DMA (Direct Memory Access)**: El volcado del buffer a la pantalla TFT se realiza mediante DMA, lo que permite que la CPU comience a procesar el siguiente frame mientras el hardware transfiere los datos. +- **Acceso a Datos de 16 bits**: Los sprites de 2bpp y 4bpp utilizan punteros `uint16_t*` para garantizar accesos alineados a memoria, lo cual es significativamente más rápido en la arquitectura Xtensa del ESP32. -## String Optimization +### 3. Optimización de TileMaps +El renderizado de mapas de tiles es una de las operaciones más costosas. PixelRoot32 utiliza: -### Avoid String Operations - -```cpp -// ❌ BAD: String concatenation -std::string text = "Score: " + std::to_string(score); - -// ✅ GOOD: Static buffer with snprintf -char buffer[32]; -snprintf(buffer, sizeof(buffer), "Score: %d", score); -renderer.drawText(buffer, 10, 10, Color::White, 1); -``` +- **Caché de Paleta**: Durante el dibujado de un tilemap, se genera una tabla de búsqueda (LUT) temporal para evitar cálculos de color redundantes por cada píxel. +- **Dibujado por Columnas**: Optimizado para minimizar los saltos de memoria en el framebuffer. -### Cache Text Rendering +### 4. Colisiones Eficientes +Usa colisiones basadas en tiles siempre que sea posible. Acceder a un array de tiles es O(1), mientras que iterar sobre una lista de entidades es O(n). ```cpp -class CachedText { -private: - char buffer[32]; - bool dirty = true; - int lastValue = -1; - -public: - void update(int value) { - if (value != lastValue) { - snprintf(buffer, sizeof(buffer), "Score: %d", value); - lastValue = value; - dirty = true; - } - } - - void draw(Renderer& renderer, int x, int y) { - if (dirty) { - renderer.drawText(buffer, x, y, Color::White, 1); - dirty = false; - } - } -}; -``` - -## Profiling - -### Measure Frame Time - -```cpp -class PerformanceMonitor { -private: - unsigned long frameTime = 0; - unsigned long maxFrameTime = 0; - unsigned long frameCount = 0; - -public: - void startFrame() { - frameTime = millis(); - } - - void endFrame() { - unsigned long elapsed = millis() - frameTime; - if (elapsed > maxFrameTime) { - maxFrameTime = elapsed; - } - frameCount++; - - // Log every 60 frames - if (frameCount % 60 == 0) { - Serial.print("Max frame time: "); - Serial.println(maxFrameTime); - maxFrameTime = 0; - } - } -}; - -// Usage -PerformanceMonitor perf; - -void update(unsigned long deltaTime) override { - perf.startFrame(); - Scene::update(deltaTime); - perf.endFrame(); +// Ejemplo de colisión rápida con el mapa +int tileX = x / 8; +int tileY = y / 8; +if (levelMap.data[tileY * levelMap.width + tileX] != 0) { + // Colisión detectada } ``` -### Identify Bottlenecks - -```cpp -#ifdef PLATFORM_ESP32 -#include - -class Profiler { -private: - unsigned long updateTime = 0; - unsigned long drawTime = 0; - unsigned long collisionTime = 0; - -public: - void startUpdate() { - updateTime = micros(); - } - - void endUpdate() { - updateTime = micros() - updateTime; - } - - void startDraw() { - drawTime = micros(); - } - - void endDraw() { - drawTime = micros() - drawTime; - } - - void log() { - Serial.print("Update: "); - Serial.print(updateTime); - Serial.print("us, Draw: "); - Serial.print(drawTime); - Serial.println("us"); - } -}; -#endif -``` - -## Best Practices Summary - -### Rendering - -- ✅ Use viewport culling -- ✅ Batch similar draw operations -- ✅ Use tilemaps for backgrounds -- ✅ Limit sprite count and size -- ✅ Organize by render layers - -### Logic - -- ✅ Cache expensive calculations -- ✅ Use lazy evaluation -- ✅ Avoid sqrt/sin/cos in loops -- ✅ Pre-calculate lookup tables -- ✅ Reduce update frequency for non-critical entities - -### Memory - -- ✅ Use object pooling -- ✅ Avoid dynamic allocation -- ✅ Store data in flash -- ✅ Reuse objects - -### Collisions - -- ✅ Use collision layers efficiently -- ✅ Only check active entities -- ✅ Use simple hitboxes -- ✅ Limit active collision pairs - -### Strings +## Recomendaciones Generales -- ✅ Use static buffers -- ✅ Cache text rendering -- ✅ Avoid string operations in loops +- **Sprites Indexados**: Prefiere `Sprite2bpp` (4 colores) o `Sprite4bpp` (16 colores) sobre `Sprite` (1bpp) si necesitas color, ya que están altamente optimizados. +- **Evitar `std::string` en el Loop**: Las concatenaciones de strings generan fragmentación de memoria. Usa buffers estáticos o `char[]` para textos dinámicos. +- **Perfilado**: Utiliza `engine.getFPS()` para monitorear el impacto de tus cambios en tiempo real. ## Common Optimization Patterns diff --git a/docs/manual/optimization/platforms_and_drivers.md b/docs/manual/optimization/platforms_and_drivers.md index 4b09c17..939e820 100644 --- a/docs/manual/optimization/platforms_and_drivers.md +++ b/docs/manual/optimization/platforms_and_drivers.md @@ -43,9 +43,18 @@ PixelRoot32 supports multiple platforms through driver abstraction. This guide c TFT_eSPI is the display driver for ESP32, supporting many TFT displays. -#### Configuration +#### Optimizaciones ESP32 + +Para maximizar el rendimiento en ESP32, PixelRoot32 utiliza: + +- **DMA (Direct Memory Access)**: Las transferencias al display se realizan en segundo plano, permitiendo que la CPU prepare el siguiente frame mientras se envía el actual. +- **Doble Buffer con IRAM**: El motor utiliza un buffer de pantalla (Sprite de TFT_eSPI) optimizado para transferencias rápidas. +- **Alineación de 16 bits**: Los datos de sprites 2bpp/4bpp están alineados a palabras de 16 bits para aprovechar la arquitectura Xtensa. +- **IRAM_ATTR**: Las funciones críticas de renderizado están marcadas para residir en la RAM de instrucciones, evitando cuellos de botella por acceso a la Flash. + +#### Configuración DMA -Configure TFT_eSPI via build flags in `platformio.ini`: +El DMA se activa automáticamente si el hardware lo soporta. Asegúrate de configurar la frecuencia SPI adecuada para tu display (usualmente 40MHz u 80MHz). ```ini [env:esp32dev] From 94fb65b94598057cdea1b25c3f5569171cf17021 Mon Sep 17 00:00:00 2001 From: Gperez88 Date: Thu, 29 Jan 2026 03:12:56 -0400 Subject: [PATCH 3/6] docs: update examples section and remove Space Invaders overview Revise the Examples section in the README to include a comprehensive table of game samples, detailing descriptions and features. Remove the Space Invaders overview from the documentation as it is no longer needed. Update mkdocs.yml to reflect this change in navigation. Additionally, introduce an FPS overlay feature in the engine documentation for performance monitoring. --- README.md | 49 +- docs/api_reference/core/engine.md | 26 + docs/examples/space_invaders/overview.md | 594 ------------------ .../manual/optimization/performance_tuning.md | 2 +- .../optimization/platforms_and_drivers.md | 5 + docs/reference/game_examples_guide.md | 69 +- mkdocs.yml | 3 - site/404.html | 2 +- .../audio/audio_config/index.html | 4 +- .../audio/audio_engine/index.html | 4 +- .../audio/audio_types/index.html | 4 +- .../audio/music_player/index.html | 4 +- site/api_reference/core/actor/index.html | 4 +- site/api_reference/core/engine/index.html | 4 +- site/api_reference/core/entity/index.html | 4 +- .../core/input_config/index.html | 4 +- .../core/input_manager/index.html | 4 +- .../core/physics_actor/index.html | 4 +- site/api_reference/core/scene/index.html | 99 +-- .../graphics/camera2d/index.html | 4 +- site/api_reference/graphics/color/index.html | 4 +- .../graphics/display_config/index.html | 4 +- site/api_reference/graphics/font/index.html | 4 +- .../graphics/renderer/index.html | 4 +- site/api_reference/graphics/sprite/index.html | 6 +- .../api_reference/graphics/tilemap/index.html | 4 +- .../physics/collision_system/index.html | 4 +- .../physics/collision_types/index.html | 4 +- site/api_reference/ui/ui_button/index.html | 4 +- site/api_reference/ui/ui_checkbox/index.html | 4 +- site/api_reference/ui/ui_element/index.html | 4 +- site/api_reference/ui/ui_label/index.html | 4 +- site/api_reference/ui/ui_layout/index.html | 4 +- .../ui/ui_layouts/anchor_layout/index.html | 4 +- .../ui/ui_layouts/grid_layout/index.html | 4 +- .../ui_layouts/horizontal_layout/index.html | 4 +- .../ui_layouts/padding_container/index.html | 4 +- .../ui/ui_layouts/panel/index.html | 4 +- .../ui/ui_layouts/vertical_layout/index.html | 4 +- site/assets/images/favicon.ico | Bin 82844 -> 882 bytes site/assets/images/logo.png | Bin 148597 -> 4048 bytes site/assets/images/logo_v2.png | Bin 44132 -> 1259 bytes .../space_invaders/overview/index.html | 229 ------- .../fundamental_concepts/index.html | 4 +- site/getting_started/installation/index.html | 4 +- .../what_is_pixelroot32/index.html | 2 +- .../why_pixelroot32/index.html | 2 +- .../your_first_project/index.html | 4 +- site/index.html | 2 +- .../cameras_and_scrolling/index.html | 4 +- .../color_palettes/index.html | 4 +- .../particles_and_effects/index.html | 4 +- .../sprites_and_animation/index.html | 4 +- .../advanced_graphics/tilemaps/index.html | 4 +- site/manual/game_development/audio/index.html | 4 +- .../basic_rendering/index.html | 4 +- .../input_and_control/index.html | 4 +- .../physics_and_collisions/index.html | 4 +- .../scenes_and_entities/index.html | 4 +- .../user_interface/index.html | 4 +- .../optimization/extensibility/index.html | 4 +- .../optimization/memory_management/index.html | 4 +- .../performance_tuning/index.html | 333 ++-------- .../platforms_and_drivers/index.html | 181 +++--- site/reference/api_overview/index.html | 4 +- site/reference/code_examples/index.html | 4 +- site/reference/game_examples_guide/index.html | 6 +- site/resources/available_tools/index.html | 51 +- site/resources/faq/index.html | 4 +- .../limitations_and_considerations/index.html | 2 +- site/resources/troubleshooting/index.html | 4 +- site/search/search_index.json | 2 +- site/sitemap.xml | 136 ++-- site/sitemap.xml.gz | Bin 881 -> 858 bytes .../advanced_features/index.html | 106 +--- .../sprite_compiler/installation/index.html | 68 +- .../tools/sprite_compiler/overview/index.html | 65 +- .../sprite_compiler/usage_guide/index.html | 53 +- .../tilemap_editor/installation/index.html | 8 +- site/tools/tilemap_editor/overview/index.html | 2 +- .../tilemap_editor/usage_guide/index.html | 2 +- 81 files changed, 630 insertions(+), 1671 deletions(-) delete mode 100644 docs/examples/space_invaders/overview.md delete mode 100644 site/examples/space_invaders/overview/index.html diff --git a/README.md b/README.md index 4cc2981..e2e2ef3 100644 --- a/README.md +++ b/README.md @@ -70,11 +70,49 @@ Complete technical reference organized by modules: - Physics (CollisionSystem, CollisionTypes) - UI (UIElement, UIButton, UILabel, Layouts) -### Examples -- Space Invaders: Complete game analysis +### Examples (Game Samples) +Documentación de los juegos y demos incluidos en [PixelRoot32 Game Samples](https://github.com/Gperez88/PixelRoot32-Game-Samples): + +| Ejemplo | Descripción | Motor / características | +|--------|-------------|---------------------------| +| **Pong** | Clásico con física y colisiones | PhysicsActor, Audio (PICO8) | +| **BrickBreaker** | Estilo Breakout, partículas | PhysicsActor, ParticleEmitter, Audio (GBC) | +| **Snake** | Juego en rejilla, pool de entidades | Entity pooling, rejilla discreta | +| **Space Invaders** | Shooter completo | 1bpp sprites, colisiones swept, música dinámica, starfield | +| **TicTacToe** | Turnos y IA simple | UI, lógica de tablero | +| **Metroidvania** | Plataformas 2D, tilemap multicapa | 4bpp sprites, TileMap4bpp, colisión tile-based, escaleras, optimizaciones ESP32 (sin cámara/scroll) | +| **CameraDemo** | Scroll horizontal y parallax | Camera2D, plataformas, capas | +| **SpritesDemo** | Formatos 2bpp y 4bpp | Sprite2bpp, Sprite4bpp, animación | +| **TileMapDemo** | Tilemaps 4bpp | TileMap4bpp, viewport culling | + +### Herramientas e implementaciones del motor + +**Herramientas usadas en los ejemplos:** + +| Herramienta | Uso | Repositorio / notas | +|-------------|-----|----------------------| +| **PixelRoot32 Sprite Compiler** | Convierte PNG a datos 1bpp/2bpp/4bpp (C++ headers) | PixelRoot32 Sprite Compiler (Python) | +| **PixelRoot32 Tilemap Editor** | Edición visual de tilemaps, export a C++ | PixelRoot32 Tilemap Editor (opcional; algunos fondos se generan en código) | + +**Implementaciones del motor en Game Samples:** + +| Componente | ESP32 | Native (PC) | +|------------|--------|-------------| +| **Display** | TFT_eSPI (ST7789 240×240), buffer + DMA | SDL2, ventana 240×240 | +| **Input** | 5 botones digitales (InputConfig) | Teclado → scancodes | +| **Audio** | DAC interno o I2S (ESP32_DAC / ESP32_I2S) | SDL2 audio | +| **Sprites** | 1bpp (siempre), 2bpp/4bpp con `PIXELROOT32_ENABLE_*` | Igual | +| **Escenas** | Scene, SceneArena (arena opcional), MAX_ENTITIES configurable | Igual | + +**Opciones de build relevantes** (en `platformio.ini` de Game Samples): + +- `PIXELROOT32_ENABLE_2BPP_SPRITES` / `PIXELROOT32_ENABLE_4BPP_SPRITES`: sprites 2bpp y 4bpp. +- `PIXELROOT32_ENABLE_SCENE_ARENA`: asignación desde arena en escena (menos `new`/`delete`). +- `MAX_ENTITIES`, `MAX_LAYERS`: límites de entidades y capas de render. ### Tools -- Sprite Compiler: Installation, usage, and advanced features +- **Sprite Compiler**: Overview, instalación, guía de uso y características avanzadas (1bpp, 2bpp, 4bpp). +- **Tilemap Editor**: Edición visual de tilemaps y export a C++ (referencia en docs cuando esté habilitada). ### Resources - Available Tools @@ -177,9 +215,8 @@ PixelRoot32-Docs/ │ │ ├── game_development/ # Core game development │ │ ├── advanced_graphics/ # Advanced graphics │ │ └── optimization/ # Optimization guides -│ ├── reference/ # Reference documentation +│ ├── reference/ # Reference documentation (incl. Game Examples Guide) │ ├── api_reference/ # Complete API reference -│ ├── examples/ # Game examples │ ├── tools/ # Tool documentation │ └── resources/ # Resources (FAQ, troubleshooting, etc.) ├── site/ # Generated HTML (git-ignored) @@ -201,4 +238,4 @@ See individual source files for license details. --- -**Last updated:** January 23, 2026 +**Last updated:** January 29, 2026 diff --git a/docs/api_reference/core/engine.md b/docs/api_reference/core/engine.md index 7073c73..77a76de 100644 --- a/docs/api_reference/core/engine.md +++ b/docs/api_reference/core/engine.md @@ -278,6 +278,32 @@ void playMusic() { } ``` +## Optional: FPS overlay + +When the engine is built with the preprocessor define **`PIXELROOT32_ENABLE_FPS_DISPLAY`**, an on-screen FPS counter is drawn each frame. + +**Behavior:** + +- A green text string `"FPS xxx"` is drawn in the top-right area (position from `Renderer::getWidth()` and a fixed Y offset). +- The value is derived from frame delta time (FPS = 1000 / deltaTime ms), clamped to 0–999. + +**Performance:** + +- The numeric value is recalculated and formatted only every **8 frames**; the cached string is drawn every frame to keep the overlay visible without extra per-frame cost (division and `snprintf` are done at most once every 8 frames). + +**How to enable:** + +In `platformio.ini`, add to your environment's `build_flags`: + +```ini +build_flags = + -D PIXELROOT32_ENABLE_FPS_DISPLAY +``` + +No code changes are required; the overlay is drawn automatically after the scene in `Engine::draw()`. The implementation uses the private method `drawFpsOverlay(Renderer& r)`, which is only compiled when the define is set. + +See also: [Performance Tuning - Profiling](../../manual/optimization/performance_tuning.md) and [Platforms and Drivers - Build flags](../../manual/optimization/platforms_and_drivers.md). + ## Usage Example ```cpp diff --git a/docs/examples/space_invaders/overview.md b/docs/examples/space_invaders/overview.md deleted file mode 100644 index 21b1c64..0000000 --- a/docs/examples/space_invaders/overview.md +++ /dev/null @@ -1,594 +0,0 @@ -# Space Invaders Example - -A complete implementation of the classic Space Invaders game showcasing advanced PixelRoot32 features including sprite animations, collision detection with sweep tests, dynamic music tempo, tilemap backgrounds, and efficient entity management. - -## Overview - -This example demonstrates a fully playable Space Invaders game with: -- Player ship with horizontal movement and shooting -- Formation of 32 aliens (4 rows × 8 columns) with different types -- Defensive bunkers that degrade when hit -- Dynamic background music that speeds up as aliens approach -- Visual explosion effects -- Score and lives system -- Win/lose conditions - -## Architecture - -### Scene Structure - -**SpaceInvadersScene** (`SpaceInvadersScene.h/cpp`) -- Main game scene managing all entities and game state -- Handles game loop, collisions, spawning, and state transitions -- Implements custom entity management to work within MAX_ENTITIES limit - -### Entity Classes - -#### PlayerActor -- **Type**: `PhysicsActor` -- **Features**: - - Horizontal movement controlled by LEFT/RIGHT buttons - - Shooting with FIRE button - - Physics-based movement with world boundaries - - Respawn system after being hit - -#### AlienActor -- **Type**: `Actor` -- **Features**: - - Three types: SQUID (top row, 30 points), CRAB (middle rows, 20 points), OCTOPUS (bottom rows, 10 points) - - Step-based sprite animations (alternates frames on movement) - - Formation movement (moves as a group, drops down when hitting edges) - - Uses MultiSprite for CRAB type (multi-layer sprite) - -#### ProjectileActor -- **Type**: `PhysicsActor` -- **Features**: - - Two types: PLAYER_BULLET (white, moves up) and ENEMY_BULLET (red, moves down) - - Object pooling (reuses projectiles instead of creating/destroying) - - Sweep test collision detection for fast-moving projectiles - - Tracks previous position for accurate collision detection - -#### BunkerActor -- **Type**: `Actor` -- **Features**: - - Health system (starts at 4, decreases when hit) - - Visual degradation (color changes: Green → Yellow → Red) - - Collision detection with dynamic hitbox based on remaining health - -### Background - -**TilemapBackground** -- Starfield created using tilemap system -- Procedurally generated pattern -- Rendered on layer 0 (background) - -## Key Systems - -### Collision Detection - -The game uses **sweep tests** for accurate collision detection with fast-moving projectiles: - -```cpp -// Example from SpaceInvadersScene::handleCollisions() -Circle startCircle; -startCircle.x = proj->getPreviousX() + radius; -startCircle.y = proj->getPreviousY() + PROJECTILE_HEIGHT * 0.5f; -startCircle.radius = radius; - -Circle endCircle; -endCircle.x = proj->x + radius; -endCircle.y = proj->y + PROJECTILE_HEIGHT * 0.5f; -endCircle.radius = radius; - -float tHit = 0.0f; -if (sweepCircleVsRect(startCircle, endCircle, targetBox, tHit)) { - // Collision detected -} -``` - -**Why sweep tests?** -- Projectiles move fast (120 px/s) -- Standard AABB can miss collisions between frames -- Sweep tests check the path between previous and current position - -### Entity Management - -Due to the MAX_ENTITIES = 32 limit, the scene uses custom entity management: - -```cpp -// Custom vectors instead of Scene's entity system -std::vector aliens; -std::vector projectiles; -std::vector bunkers; -``` - -**Object Pooling:** -- Projectiles are pre-allocated (MaxProjectiles = 12) -- Projectiles are reused instead of created/destroyed -- `reset()` method reactivates projectiles - -**Scene Arena (Optional):** -- Uses `PIXELROOT32_ENABLE_SCENE_ARENA` if available -- Pre-allocates memory for all entities -- Avoids heap fragmentation - -### Dynamic Music Tempo - -The background music tempo increases as aliens get closer to the player: - -```cpp -void SpaceInvadersScene::updateMusicTempo() { - // Find lowest active alien - float lowestY = /* ... */; - - // Calculate threat factor (0.0 to 1.0) - float threatFactor = (lowestY - ALIEN_START_Y) * INV_Y_RANGE; - - // Target tempo: 1.0 (slow) to 1.9 (fast) - float targetTempo = 1.0f + (threatFactor * 0.9f); - - // Smooth interpolation - currentMusicTempoFactor += (targetTempo - currentMusicTempoFactor) * 0.05f; - - engine.getMusicPlayer().setTempoFactor(currentMusicTempoFactor); -} -``` - -**Music Tracks:** -- `BGM_SLOW_TRACK`: Initial tempo (slow) -- `BGM_MEDIUM_TRACK`: Medium tempo (unused, can be used for transitions) -- `BGM_FAST_TRACK`: Fast tempo (unused, can be used for transitions) -- `WIN_TRACK`: Victory music (plays once) -- `GAME_OVER_TRACK`: Defeat music (plays once) - -### Visual Effects - -#### Enemy Explosions -- Simple cross pattern (horizontal + vertical lines) -- Pool of 8 explosion effects -- 200ms duration each -- Reused slots to avoid allocations - -#### Player Explosion -- 3-frame sprite animation -- Plays when player is hit -- Pauses gameplay during animation -- Respawns player after animation completes - -### Alien Formation Movement - -Aliens move in lockstep formation: - -```cpp -void SpaceInvadersScene::updateAliens(unsigned long deltaTime) { - stepTimer += scaledDelta; - - if (stepTimer >= stepDelay) { - // Check if formation hit edge - bool edgeHit = /* ... */; - - if (edgeHit) { - moveDirection *= -1; // Reverse direction - // Drop down - for (auto* alien : aliens) { - alien->move(0, ALIEN_DROP_AMOUNT); - } - } else { - // Move horizontally - float dx = moveDirection * ALIEN_STEP_AMOUNT_X; - for (auto* alien : aliens) { - alien->move(dx, 0); - } - } - } -} -``` - -**Movement Characteristics:** -- Step-based (not continuous) -- Base step delay: 417ms (72 BPM) -- Step amount: 2.5 pixels horizontally -- Drop amount: 7 pixels when hitting edge -- Tempo scales with music tempo factor - -### Enemy Shooting System - -Enemies shoot from bottom-most aliens: - -```cpp -void SpaceInvadersScene::enemyShoot() { - // Find bottom-most aliens (no alien below them) - std::vector bottomAliens; - // ... collect bottom aliens ... - - // Difficulty-based chance (increases as aliens die) - float t = 1.0f - (alive / total); - int chance = minChance + (t * (maxChance - minChance)); - - // Random roll - if (roll >= chance) { - // Fire from random bottom alien - } -} -``` - -**Shooting Rules:** -- Only bottom-most aliens can shoot -- Maximum 4 enemy bullets active at once -- Chance increases as more aliens are destroyed -- Difficulty scales from 8% to 30% chance - -## Code Structure - -### File Organization - -``` -SpaceInvaders/ -├── SpaceInvadersScene.h/cpp # Main scene -├── PlayerActor.h/cpp # Player ship -├── AlienActor.h/cpp # Enemy aliens -├── ProjectileActor.h/cpp # Bullets -├── BunkerActor.h/cpp # Defensive bunkers -├── GameConstants.h # Game configuration -└── AlienSprites.h # Sprite definitions -``` - -### Game Constants - -Key constants defined in `GameConstants.h`: - -```cpp -// Formation -constexpr int ALIEN_ROWS = 4; -constexpr int ALIEN_COLS = 8; - -// Movement -constexpr unsigned long BASE_STEP_DELAY = 417; // 72 BPM -constexpr float ALIEN_STEP_AMOUNT_X = 2.5f; -constexpr float ALIEN_DROP_AMOUNT = 7.0f; - -// Projectiles -constexpr int MaxProjectiles = 12; -constexpr float PROJECTILE_SPEED = 120.0f; - -// Bunkers -constexpr int BUNKER_COUNT = 4; -constexpr int BUNKER_WIDTH = 24; -constexpr int BUNKER_HEIGHT = 16; -``` - -## Design Patterns Used - -### 1. Object Pooling - -Projectiles are pooled to avoid allocations: - -```cpp -// Pre-allocate in resetGame() -for (int i = 0; i < MaxProjectiles; ++i) { - ProjectileActor* projectile = new ProjectileActor(0, -PROJECTILE_HEIGHT, ProjectileType::PLAYER_BULLET); - projectile->deactivate(); - projectiles.push_back(projectile); -} - -// Reuse when shooting -for (auto* proj : projectiles) { - if (!proj->isActive()) { - proj->reset(px, py, ProjectileType::PLAYER_BULLET); - break; - } -} -``` - -### 2. State Machine - -Game states: Playing → Paused (on hit) → Game Over - -```cpp -if (gameOver) { - // Game over state -} else if (isPaused) { - // Paused during player explosion - if (!playerExplosion.isActive()) { - respawnPlayerUnderBunker(); - isPaused = false; - } -} else { - // Normal gameplay -} -``` - -### 3. Formation Controller - -Aliens are moved as a group by the scene, not individually: - -```cpp -// Scene controls movement -for (auto* alien : aliens) { - if (alien->isActive()) { - alien->move(dx, 0); // Scene tells alien to move - } -} -``` - -### 4. Explosion Pool - -Enemy explosions use a fixed pool: - -```cpp -static constexpr int MaxEnemyExplosions = 8; -EnemyExplosion enemyExplosions[MaxEnemyExplosions]; - -void spawnEnemyExplosion(float x, float y) { - // Find first available slot - for (int i = 0; i < MaxEnemyExplosions; ++i) { - if (!enemyExplosions[i].active) { - enemyExplosions[i].active = true; - enemyExplosions[i].x = x; - enemyExplosions[i].y = y; - enemyExplosions[i].remainingMs = 200; - return; - } - } -} -``` - -## Audio Integration - -### Sound Effects - -**Player Shoot:** -```cpp -AudioEvent event{}; -event.type = WaveType::PULSE; -event.frequency = 880.0f; -event.duration = 0.08f; -event.volume = 0.4f; -event.duty = 0.5f; -engine.getAudioEngine().playEvent(event); -``` - -**Enemy Hit:** -```cpp -AudioEvent event{}; -event.type = WaveType::NOISE; -event.frequency = 600.0f; -event.duration = 0.12f; -event.volume = 0.6f; -engine.getAudioEngine().playEvent(event); -``` - -**Player Hit:** -```cpp -AudioEvent event{}; -event.type = WaveType::NOISE; -event.frequency = 400.0f; -event.duration = 0.18f; -event.volume = 0.7f; -engine.getAudioEngine().playEvent(event); -``` - -### Background Music - -- Uses `MusicPlayer` with tempo control -- Tempo dynamically adjusts based on threat level -- Different tracks for win/lose conditions - -## Sprite System - -### Sprite Formats - -- **1bpp sprites**: Player, most aliens, explosions -- **MultiSprite**: CRAB alien type (multi-layer sprite) -- **Animations**: Step-based (advances on movement, not time) - -### Sprite Scaling - -All sprites use `SPRITE_SCALE = 1.25f` for larger appearance: - -```cpp -renderer.drawSprite(PLAYER_SHIP_SPRITE, - static_cast(x), - static_cast(y), - SPRITE_SCALE, // scaleX - SPRITE_SCALE, // scaleY - Color::White); -``` - -## Performance Optimizations - -### 1. Entity Pooling -- Projectiles are pooled (12 max) -- Explosions are pooled (8 max) -- Avoids allocations during gameplay - -### 2. Scene Arena (Optional) -- Pre-allocates 8KB buffer for entities -- All entities allocated from arena -- Zero heap fragmentation - -### 3. Efficient Collision Detection -- Sweep tests only for fast projectiles -- Simple AABB for static/slow objects -- Early exits in collision loops - -### 4. Custom Entity Management -- Bypasses Scene's entity limit by using vectors -- Direct control over entity lifecycle -- More efficient for this specific use case - -## Game Flow - -### Initialization -1. Create tilemap background -2. Spawn player at bottom center -3. Spawn 32 aliens in formation (4×8 grid) -4. Spawn 4 bunkers evenly spaced -5. Pre-allocate projectile pool -6. Start background music - -### Gameplay Loop -1. **Update Input**: Read player movement and shooting -2. **Update Aliens**: Move formation, check edges, enemy shooting -3. **Update Projectiles**: Move projectiles, check boundaries -4. **Handle Collisions**: Sweep tests for projectiles vs aliens/bunkers/player -5. **Update Effects**: Explosion animations -6. **Update Music**: Adjust tempo based on alien position -7. **Draw**: Background, entities, explosions, HUD - -### Game Over Conditions -- **Win**: All aliens destroyed -- **Lose**: Player lives reach 0 OR aliens reach player Y position - -### Respawn System -1. Player hit → Start explosion animation -2. Pause gameplay -3. Wait for explosion to complete -4. Respawn player under first intact bunker -5. Resume gameplay - -## Key Learnings - -### What This Example Demonstrates - -1. **Advanced Collision Detection** - - Sweep tests for fast-moving objects - - Accurate hit detection even at high speeds - -2. **Efficient Entity Management** - - Object pooling for frequently created/destroyed entities - - Custom management to work around MAX_ENTITIES limit - - Scene Arena for zero-fragmentation allocation - -3. **Dynamic Audio** - - Music tempo synchronization with gameplay - - Multiple music tracks for different states - - Sound effects for game events - -4. **Sprite Animations** - - Step-based animations (tied to game logic, not time) - - MultiSprite for complex sprites - - Sprite scaling for visual variety - -5. **State Management** - - Game states (playing, paused, game over) - - Smooth transitions between states - - Respawn system with visual feedback - -6. **Formation Movement** - - Coordinated group movement - - Edge detection and direction reversal - - Progressive difficulty (tempo increases) - -7. **Visual Effects** - - Explosion animations - - Health-based rendering (bunkers) - - HUD with score and lives - -## Code Examples - -### Shooting System - -```cpp -// Player shooting -if (fireInputReady && player->wantsToShoot()) { - // Check if player bullet already active - bool hasPlayerBullet = false; - for (auto* proj : projectiles) { - if (proj->isActive() && proj->getType() == ProjectileType::PLAYER_BULLET) { - hasPlayerBullet = true; - break; - } - } - - if (!hasPlayerBullet) { - // Find inactive projectile and reuse it - for (auto* proj : projectiles) { - if (!proj->isActive()) { - float px = player->x + (PLAYER_WIDTH - PROJECTILE_WIDTH) / 2.0f; - float py = player->y - PROJECTILE_HEIGHT; - proj->reset(px, py, ProjectileType::PLAYER_BULLET); - - // Play shoot sound - AudioEvent event{}; - event.type = WaveType::PULSE; - event.frequency = 880.0f; - event.duration = 0.08f; - event.volume = 0.4f; - engine.getAudioEngine().playEvent(event); - break; - } - } - } -} -``` - -### Collision Detection with Sweep Test - -```cpp -// Check projectile vs alien collision -Circle startCircle; -startCircle.x = proj->getPreviousX() + radius; -startCircle.y = proj->getPreviousY() + PROJECTILE_HEIGHT * 0.5f; -startCircle.radius = radius; - -Circle endCircle; -endCircle.x = proj->x + radius; -endCircle.y = proj->y + PROJECTILE_HEIGHT * 0.5f; -endCircle.radius = radius; - -float tHit = 0.0f; -pixelroot32::core::Rect targetBox = alien->getHitBox(); - -if (sweepCircleVsRect(startCircle, endCircle, targetBox, tHit) || - proj->getHitBox().intersects(targetBox)) { - // Hit! Deactivate projectile and kill alien - proj->deactivate(); - alien->kill(); - score += alien->getScoreValue(); - spawnEnemyExplosion(alien->x + alien->width * 0.5f, - alien->y + alien->height * 0.5f); -} -``` - -## Running the Example - -### Prerequisites -- PixelRoot32 Game Engine installed -- ESP32 or Native build configured -- Display and input configured - -### Build and Run - -1. **Include the example in your project:** - ```cpp - #include "examples/SpaceInvaders/SpaceInvadersScene.h" - ``` - -2. **In your main.cpp:** - ```cpp - spaceinvaders::SpaceInvadersScene gameScene; - - void setup() { - engine.init(); - gameScene.init(); - engine.setScene(&gameScene); - } - ``` - -3. **Build and upload** (ESP32) or **run** (Native) - -### Controls - -- **LEFT/RIGHT**: Move player ship -- **FIRE**: Shoot projectile -- **FIRE** (on game over): Restart game - -## See Also - -- [Game Examples Guide](../../reference/game_examples_guide.md) - Analysis of all game examples -- [Manual - Scenes and Entities](../../manual/game_development/scenes_and_entities.md) - Scene system -- [Manual - Physics and Collisions](../../manual/game_development/physics_and_collisions.md) - Collision detection -- [Manual - Audio](../../manual/game_development/audio.md) - Audio system -- [Manual - Sprites and Animation](../../manual/advanced_graphics/sprites_and_animation.md) - Sprite system -- [Manual - Memory Management](../../manual/optimization/memory_management.md) - Object pooling diff --git a/docs/manual/optimization/performance_tuning.md b/docs/manual/optimization/performance_tuning.md index 3eb2fd6..a8323ba 100644 --- a/docs/manual/optimization/performance_tuning.md +++ b/docs/manual/optimization/performance_tuning.md @@ -71,7 +71,7 @@ if (levelMap.data[tileY * levelMap.width + tileX] != 0) { - **Sprites Indexados**: Prefiere `Sprite2bpp` (4 colores) o `Sprite4bpp` (16 colores) sobre `Sprite` (1bpp) si necesitas color, ya que están altamente optimizados. - **Evitar `std::string` en el Loop**: Las concatenaciones de strings generan fragmentación de memoria. Usa buffers estáticos o `char[]` para textos dinámicos. -- **Perfilado**: Utiliza `engine.getFPS()` para monitorear el impacto de tus cambios en tiempo real. +- **Perfilado**: Activa el overlay de FPS compilando con `PIXELROOT32_ENABLE_FPS_DISPLAY` (ver [Engine - FPS overlay](../../api_reference/core/engine.md#optional-fps-overlay)) para monitorear el impacto de tus cambios en tiempo real. ## Common Optimization Patterns diff --git a/docs/manual/optimization/platforms_and_drivers.md b/docs/manual/optimization/platforms_and_drivers.md index 939e820..88b22c7 100644 --- a/docs/manual/optimization/platforms_and_drivers.md +++ b/docs/manual/optimization/platforms_and_drivers.md @@ -218,8 +218,13 @@ build_flags = -D PIXELROOT32_ENABLE_2BPP_SPRITES # Enable 2bpp sprite format -D PIXELROOT32_ENABLE_4BPP_SPRITES # Enable 4bpp sprite format -D PIXELROOT32_ENABLE_SCENE_ARENA # Enable Scene Arena (experimental) + -D PIXELROOT32_ENABLE_FPS_DISPLAY # On-screen FPS counter (green, top-right; value updated every 8 frames) ``` +### FPS overlay (PIXELROOT32_ENABLE_FPS_DISPLAY) + +When defined, the engine draws an on-screen FPS counter (green text, top-right) each frame. The value is recalculated every 8 frames to keep per-frame cost low. No code changes are required; the overlay is drawn automatically after the scene. See [API Reference - Engine - Optional: FPS overlay](../../api_reference/core/engine.md#optional-fps-overlay) for details. + ### Scene limits (MAX_LAYERS / MAX_ENTITIES) You can override the default scene limits from your project without modifying the engine. The default of **3** for `MAX_LAYERS` is due to **ESP32 platform constraints** (memory and draw-loop cost); on native/PC you can use a higher value. diff --git a/docs/reference/game_examples_guide.md b/docs/reference/game_examples_guide.md index 54a2ec8..0df17d6 100644 --- a/docs/reference/game_examples_guide.md +++ b/docs/reference/game_examples_guide.md @@ -4,14 +4,17 @@ This guide analyzes the complete game examples included with PixelRoot32, explai ## Available Examples -PixelRoot32 includes several complete game examples: - -- **Space Invaders**: Full-featured shooter with enemies, projectiles, and audio -- **Pong**: Classic arcade game with physics and collisions -- **Snake**: Grid-based game with entity pooling -- **TicTacToe**: Turn-based game with simple AI -- **CameraDemo**: Platformer with scrolling camera and parallax -- **SpritesDemo**: Demonstration of advanced sprite formats +PixelRoot32 (en el proyecto [PixelRoot32 Game Samples](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Sampless)) incluye estos juegos y demos: + +- **Metroidvania**: Plataformas 2D con tilemap 4bpp multicapa y colisión tile-based (requiere `PIXELROOT32_ENABLE_4BPP_SPRITES`; sin scroll/cámara) +- **Space Invaders**: Shooter completo con enemigos, proyectiles, búnkeres y audio +- **Pong**: Clásico con física y colisiones +- **BrickBreaker**: Estilo Breakout con partículas y audio avanzado +- **Snake**: Juego en rejilla con entity pooling +- **TicTacToe**: Turnos y IA simple +- **CameraDemo**: Plataformas con cámara y parallax +- **SpritesDemo**: Sprites 2bpp y 4bpp +- **TileMapDemo**: Tilemaps 4bpp (con viewport culling) ## Space Invaders @@ -25,7 +28,7 @@ Space Invaders demonstrates a complete game with multiple systems: - **Actor Hierarchy**: `PlayerActor`, `AlienActor`, `ProjectileActor`, `BunkerActor` - **Collision System**: Uses collision layers for player, enemies, projectiles - **Audio Integration**: Sound effects for shooting, explosions, music -- **Tilemap Background**: Starfield using tilemap system +- **Background**: Starfield (patrón de estrellas en código) o tilemap ### Key Systems @@ -71,9 +74,41 @@ projectile->setCollisionMask(Layers::ALIEN | Layers::BUNKER); - Collision layers are essential for complex games - Object pooling improves performance -- Tilemaps are efficient for backgrounds +- Starfield or tilemap backgrounds are efficient - Audio enhances game feel significantly +## Metroidvania + +**Ubicación**: `src/examples/Games/Metroidvania/` + +### Arquitectura + +Metroidvania es un ejemplo de plataformas 2D con tilemap multicapa y optimizaciones pensadas para ESP32. **No usa scroll ni cámara**; el nivel se dibuja con origen fijo (0,0). + +- **Scene**: `MetroidvaniaScene` con un único `PlayerActor` y varias capas de tilemap (background, platforms, details, stairs). +- **PlayerActor**: Movimiento horizontal y vertical, escaleras, colisión **tile-based** (sin listas de rectángulos). +- **Tilemap**: 4bpp (`TileMap4bpp`), con viewport culling y caché de paleta en el motor. Nivel 40×30 tiles (320×240 px). +- **Sin cámara**: La vista no sigue al jugador; para scroll habría que usar `Camera2D` y aplicar offset en el renderer (como en CameraDemo). + +### Características del motor usadas + +- **Colisión tile-based**: Comprobación directa de tiles alrededor del jugador (`getTileAt`), en lugar de iterar sobre `platformRects`. +- **Sprites 4bpp**: Player con animaciones (idle, run, jump) desde headers generados (p. ej. Sprite Compiler). +- **Optimizaciones de renderizado**: Viewport culling en `drawTileMap`, `drawSprite` 4bpp optimizado, capas culleadas por viewport. +- **Opcional**: Scene arena, DMA, IRAM_ATTR en rutas críticas (según plan de optimización del ejemplo). + +### Patrones utilizados + +- **Tile-based collision**: Un solo acceso por tile en O(1) en lugar de O(N) rectángulos. +- **Detección de escaleras**: Un solo resultado reutilizado para colisión y cambio de estado. +- **Hitbox simplificada**: Menos puntos de comprobación vertical (cabeza y pies). + +### Lecciones + +- La colisión tile-based escala mejor que listas de rectángulos en niveles grandes. +- Las optimizaciones de viewport y 4bpp mejoran FPS en ESP32. +- Metroidvania sirve de referencia para juegos de plataformas con tilemap y cámara. + ## Pong **Location**: `src/examples/Pong/` @@ -446,18 +481,20 @@ void draw(pixelroot32::graphics::Renderer& renderer) override { ### Beginner Examples -1. **Pong**: Start here - simple physics and collisions -2. **TicTacToe**: Learn turn-based logic -3. **Snake**: Understand entity pooling +1. **Pong**: Física y colisiones básicas +2. **TicTacToe**: Lógica por turnos +3. **Snake**: Entity pooling y rejilla ### Intermediate Examples -4. **CameraDemo**: Learn scrolling and parallax -5. **SpritesDemo**: Explore sprite formats +4. **CameraDemo**: Cámara y parallax +5. **SpritesDemo**: Formatos 2bpp y 4bpp +6. **BrickBreaker**: Física, partículas y audio ### Advanced Examples -6. **Space Invaders**: Complete game with all systems +7. **Space Invaders**: Juego completo (sprites 1bpp, colisiones, audio) +8. **Metroidvania**: Plataformas con tilemap 4bpp multicapa, colisión tile-based y optimizaciones ESP32 (sin scroll/cámara) ## Code Study Recommendations diff --git a/mkdocs.yml b/mkdocs.yml index cba02b6..9f85332 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -72,9 +72,6 @@ nav: - Physics: - CollisionSystem: api_reference/physics/collision_system.md - Collision Types: api_reference/physics/collision_types.md - - Examples: - - Space Invaders: - - Overview: examples/space_invaders/overview.md - Tools: - Sprite Compiler: - Overview: tools/sprite_compiler/overview.md diff --git a/site/404.html b/site/404.html index 77e2415..8ff2c8f 100644 --- a/site/404.html +++ b/site/404.html @@ -1 +1 @@ - PixelRoot32 Documentation

404 - Not found

\ No newline at end of file + PixelRoot32 Documentation
\ No newline at end of file diff --git a/site/api_reference/audio/audio_config/index.html b/site/api_reference/audio/audio_config/index.html index 4f33636..7b53c69 100644 --- a/site/api_reference/audio/audio_config/index.html +++ b/site/api_reference/audio/audio_config/index.html @@ -1,4 +1,4 @@ - AudioConfig - PixelRoot32 Documentation

AudioConfig

Configuration for the Audio subsystem.

Description

AudioConfig is a simple struct that holds configuration settings for the audio system, including the audio backend and sample rate. It is passed to AudioEngine during construction.

Namespace

namespace pixelroot32::audio {
+ AudioConfig - PixelRoot32 Documentation      

AudioConfig

Configuration for the Audio subsystem.

Description

AudioConfig is a simple struct that holds configuration settings for the audio system, including the audio backend and sample rate. It is passed to AudioEngine during construction.

Namespace

namespace pixelroot32::audio {
     struct AudioConfig {
         // ...
     };
@@ -84,4 +84,4 @@
     engine.init();
     engine.run();
 }
-

Platform-Specific Considerations

ESP32 DAC Backend

  • Sample rate: 11025 Hz recommended (lower CPU usage)
  • Quality: Lower quality, but simple setup
  • Pin: Uses GPIO 25 or 26
  • Hardware: Requires simple amplifier circuit

ESP32 I2S Backend

  • Sample rate: 22050 Hz recommended
  • Quality: Higher quality than DAC
  • Pins: Requires I2S pins (BCLK, LRCK, DOUT)
  • Hardware: Requires external I2S DAC

Native SDL2 Backend

  • Sample rate: 44100 Hz typical
  • Quality: High quality
  • Setup: Requires SDL2 library installed
  • Platforms: Windows, Linux, macOS

Performance Considerations

  • Sample rate: Lower rates use less CPU and memory
  • Backend choice: DAC is simpler but lower quality than I2S
  • Buffer size: Configured in backend, affects latency vs stability

See Also

\ No newline at end of file +

Platform-Specific Considerations

ESP32 DAC Backend

  • Sample rate: 11025 Hz recommended (lower CPU usage)
  • Quality: Lower quality, but simple setup
  • Pin: Uses GPIO 25 or 26
  • Hardware: Requires simple amplifier circuit

ESP32 I2S Backend

  • Sample rate: 22050 Hz recommended
  • Quality: Higher quality than DAC
  • Pins: Requires I2S pins (BCLK, LRCK, DOUT)
  • Hardware: Requires external I2S DAC

Native SDL2 Backend

  • Sample rate: 44100 Hz typical
  • Quality: High quality
  • Setup: Requires SDL2 library installed
  • Platforms: Windows, Linux, macOS

Performance Considerations

  • Sample rate: Lower rates use less CPU and memory
  • Backend choice: DAC is simpler but lower quality than I2S
  • Buffer size: Configured in backend, affects latency vs stability

See Also

\ No newline at end of file diff --git a/site/api_reference/audio/audio_engine/index.html b/site/api_reference/audio/audio_engine/index.html index 482e958..f7bab94 100644 --- a/site/api_reference/audio/audio_engine/index.html +++ b/site/api_reference/audio/audio_engine/index.html @@ -1,4 +1,4 @@ - AudioEngine - PixelRoot32 Documentation

AudioEngine

Core class for the NES-like audio subsystem.

Description

AudioEngine manages the audio channels (Pulse, Triangle, Noise), mixes their output, and provides the audio stream to the backend. It implements a NES-like audio system with 4 fixed channels: 2 Pulse channels, 1 Triangle channel, and 1 Noise channel.

The engine is event-driven: you trigger sound effects via playEvent(), and the engine automatically manages channel allocation and playback.

Namespace

namespace pixelroot32::audio {
+ AudioEngine - PixelRoot32 Documentation      

AudioEngine

Core class for the NES-like audio subsystem.

Description

AudioEngine manages the audio channels (Pulse, Triangle, Noise), mixes their output, and provides the audio stream to the backend. It implements a NES-like audio system with 4 fixed channels: 2 Pulse channels, 1 Triangle channel, and 1 Noise channel.

The engine is event-driven: you trigger sound effects via playEvent(), and the engine automatically manages channel allocation and playback.

Namespace

namespace pixelroot32::audio {
     class AudioEngine {
         // ...
     };
@@ -88,4 +88,4 @@
         }
     }
 };
-

Performance Considerations

  • Channel limit: Only 4 channels total; plan sound effects accordingly
  • Event dropping: If all channels are busy, new events are silently dropped
  • Update frequency: update() must be called every frame for proper timing
  • Sample generation: generateSamples() is called by backend at audio rate (not game rate)

ESP32 Considerations

  • Sample rate: Lower sample rates (11025 Hz) use less CPU and memory
  • Backend choice: DAC backend is simpler but lower quality than I2S
  • Buffer size: Larger buffers reduce underruns but increase latency
  • Channel management: Limit simultaneous sounds to avoid channel conflicts

See Also

\ No newline at end of file +

Performance Considerations

  • Channel limit: Only 4 channels total; plan sound effects accordingly
  • Event dropping: If all channels are busy, new events are silently dropped
  • Update frequency: update() must be called every frame for proper timing
  • Sample generation: generateSamples() is called by backend at audio rate (not game rate)

ESP32 Considerations

  • Sample rate: Lower sample rates (11025 Hz) use less CPU and memory
  • Backend choice: DAC backend is simpler but lower quality than I2S
  • Buffer size: Larger buffers reduce underruns but increase latency
  • Channel management: Limit simultaneous sounds to avoid channel conflicts

See Also

\ No newline at end of file diff --git a/site/api_reference/audio/audio_types/index.html b/site/api_reference/audio/audio_types/index.html index b986f1d..50f247c 100644 --- a/site/api_reference/audio/audio_types/index.html +++ b/site/api_reference/audio/audio_types/index.html @@ -1,4 +1,4 @@ - Audio Types - PixelRoot32 Documentation

Audio Types

Data structures and types for the audio system.

Description

This document describes the data structures used by the audio system, including wave types, audio events, and channel state.

Namespace

namespace pixelroot32::audio {
+ Audio Types - PixelRoot32 Documentation      

Audio Types

Data structures and types for the audio system.

Description

This document describes the data structures used by the audio system, including wave types, audio events, and channel state.

Namespace

namespace pixelroot32::audio {
     // Types and structures
 }
 

WaveType Enum

Defines the types of waveforms available.

Values: - WaveType::PULSE: Pulse wave (square wave with variable duty cycle) - WaveType::TRIANGLE: Triangle wave (smooth, melodic) - WaveType::NOISE: Noise wave (random, percussive)

Example:

pixelroot32::audio::WaveType wave = pixelroot32::audio::WaveType::PULSE;
@@ -97,4 +97,4 @@
         delay(50);  // Small delay between events
     }
 }
-

Performance Considerations

  • Event creation: Creating events is fast (just struct initialization)
  • Channel allocation: Events are queued and played when channels are available
  • Frequency range: Keep frequencies in reasonable range (100-5000 Hz) for best results
  • Duration: Shorter durations free channels faster

ESP32 Considerations

  • Memory: Events are small structs, safe to create frequently
  • CPU: Audio generation is efficient but limit simultaneous sounds
  • Quality: Lower sample rates reduce CPU usage

See Also

\ No newline at end of file +

Performance Considerations

  • Event creation: Creating events is fast (just struct initialization)
  • Channel allocation: Events are queued and played when channels are available
  • Frequency range: Keep frequencies in reasonable range (100-5000 Hz) for best results
  • Duration: Shorter durations free channels faster

ESP32 Considerations

  • Memory: Events are small structs, safe to create frequently
  • CPU: Audio generation is efficient but limit simultaneous sounds
  • Quality: Lower sample rates reduce CPU usage

See Also

\ No newline at end of file diff --git a/site/api_reference/audio/music_player/index.html b/site/api_reference/audio/music_player/index.html index 2a66a46..f402685 100644 --- a/site/api_reference/audio/music_player/index.html +++ b/site/api_reference/audio/music_player/index.html @@ -1,4 +1,4 @@ - MusicPlayer - PixelRoot32 Documentation

MusicPlayer

Lightweight sequencer built on top of AudioEngine to play background melodies as tracks.

Description

MusicPlayer is a simple sequencer that plays MusicTrack structures. It advances notes based on game time, converts MusicNote entries to AudioEvent calls, and manages playback state (play, stop, pause, resume).

The player uses one audio channel (typically a Pulse channel) for music, leaving other channels available for sound effects.

Namespace

namespace pixelroot32::audio {
+ MusicPlayer - PixelRoot32 Documentation      

MusicPlayer

Lightweight sequencer built on top of AudioEngine to play background melodies as tracks.

Description

MusicPlayer is a simple sequencer that plays MusicTrack structures. It advances notes based on game time, converts MusicNote entries to AudioEvent calls, and manages playback state (play, stop, pause, resume).

The player uses one audio channel (typically a Pulse channel) for music, leaving other channels available for sound effects.

Namespace

namespace pixelroot32::audio {
     class MusicPlayer {
         // ...
     };
@@ -106,4 +106,4 @@
         music.stop();
     }
 };
-

Performance Considerations

  • One channel: Music uses one channel, leaving others for sound effects
  • Update frequency: update() must be called every frame
  • Track size: Larger tracks use more memory (store in flash)
  • Tempo factor: Changing tempo is fast (just a multiplier)

ESP32 Considerations

  • Memory: Store tracks in flash (const/constexpr) to save RAM
  • CPU: Music playback is lightweight (simple sequencing)
  • Channel conflict: Music and sound effects share channels; plan accordingly

See Also

\ No newline at end of file +

Performance Considerations

  • One channel: Music uses one channel, leaving others for sound effects
  • Update frequency: update() must be called every frame
  • Track size: Larger tracks use more memory (store in flash)
  • Tempo factor: Changing tempo is fast (just a multiplier)

ESP32 Considerations

  • Memory: Store tracks in flash (const/constexpr) to save RAM
  • CPU: Music playback is lightweight (simple sequencing)
  • Channel conflict: Music and sound effects share channels; plan accordingly

See Also

\ No newline at end of file diff --git a/site/api_reference/core/actor/index.html b/site/api_reference/core/actor/index.html index bba57f9..dace5aa 100644 --- a/site/api_reference/core/actor/index.html +++ b/site/api_reference/core/actor/index.html @@ -1,4 +1,4 @@ - Actor - PixelRoot32 Documentation

Actor

An Entity capable of physical interaction and collision.

Description

Actor extends Entity with collision layers and masks. Actors are used for dynamic game objects like players, enemies, projectiles, and obstacles that need to interact with each other through collision detection.

Actors participate in the collision system and can detect collisions with other actors based on their collision layers and masks.

Namespace

namespace pixelroot32::core {
+ Actor - PixelRoot32 Documentation      

Actor

An Entity capable of physical interaction and collision.

Description

Actor extends Entity with collision layers and masks. Actors are used for dynamic game objects like players, enemies, projectiles, and obstacles that need to interact with each other through collision detection.

Actors participate in the collision system and can detect collisions with other actors based on their collision layers and masks.

Namespace

namespace pixelroot32::core {
     class Actor : public Entity {
         // ...
     };
@@ -126,4 +126,4 @@
         }
     }
 };
-

Performance Considerations

  • Collision layers: Use layers efficiently to reduce collision checks
  • Hitbox size: Keep hitboxes simple (AABB) for best performance
  • Collision callbacks: Keep onCollision() fast; avoid expensive operations
  • Layer organization: Group actors by layer to minimize checks

ESP32 Considerations

  • Collision checks: Collision system automatically optimizes using layers
  • Memory: Each actor consumes memory; stay within MAX_ENTITIES limit
  • Object pooling: Reuse actors instead of creating/destroying frequently

See Also

\ No newline at end of file +

Performance Considerations

  • Collision layers: Use layers efficiently to reduce collision checks
  • Hitbox size: Keep hitboxes simple (AABB) for best performance
  • Collision callbacks: Keep onCollision() fast; avoid expensive operations
  • Layer organization: Group actors by layer to minimize checks

ESP32 Considerations

  • Collision checks: Collision system automatically optimizes using layers
  • Memory: Each actor consumes memory; stay within MAX_ENTITIES limit
  • Object pooling: Reuse actors instead of creating/destroying frequently

See Also

\ No newline at end of file diff --git a/site/api_reference/core/engine/index.html b/site/api_reference/core/engine/index.html index c186e00..317fba2 100644 --- a/site/api_reference/core/engine/index.html +++ b/site/api_reference/core/engine/index.html @@ -1,4 +1,4 @@ - Engine - PixelRoot32 Documentation

Engine

The main engine class that manages the game loop and core subsystems.

Description

Engine acts as the central hub of the PixelRoot32 game engine. It initializes and manages the Renderer, InputManager, AudioEngine, and SceneManager. It runs the main game loop, handling timing (delta time), updating the current scene, and rendering frames.

The engine provides a unified interface for both ESP32 and Native (SDL2) platforms, abstracting platform-specific details while maintaining consistent behavior.

Namespace

namespace pixelroot32::core {
+ Engine - PixelRoot32 Documentation      

Engine

The main engine class that manages the game loop and core subsystems.

Description

Engine acts as the central hub of the PixelRoot32 game engine. It initializes and manages the Renderer, InputManager, AudioEngine, and SceneManager. It runs the main game loop, handling timing (delta time), updating the current scene, and rendering frames.

The engine provides a unified interface for both ESP32 and Native (SDL2) platforms, abstracting platform-specific details while maintaining consistent behavior.

Namespace

namespace pixelroot32::core {
     class Engine {
         // ...
     };
@@ -109,4 +109,4 @@
     // Run game loop
     engine.run();
 }
-

Performance Considerations

  • Initialization: init() should be called once at startup, not in the game loop
  • Scene switching: Switching scenes is fast but avoid doing it every frame
  • Subsystem access: Getters are inline and very fast; safe to call every frame
  • Delta time: Use getDeltaTime() for frame-rate independent movement

ESP32 Considerations

  • Ensure init() completes before run() to avoid initialization issues
  • Monitor memory usage when switching scenes frequently
  • Use getDeltaTime() for consistent gameplay across different frame rates

See Also

\ No newline at end of file +

Performance Considerations

  • Initialization: init() should be called once at startup, not in the game loop
  • Scene switching: Switching scenes is fast but avoid doing it every frame
  • Subsystem access: Getters are inline and very fast; safe to call every frame
  • Delta time: Use getDeltaTime() for frame-rate independent movement

ESP32 Considerations

  • Ensure init() completes before run() to avoid initialization issues
  • Monitor memory usage when switching scenes frequently
  • Use getDeltaTime() for consistent gameplay across different frame rates

See Also

\ No newline at end of file diff --git a/site/api_reference/core/entity/index.html b/site/api_reference/core/entity/index.html index c004bee..3bae716 100644 --- a/site/api_reference/core/entity/index.html +++ b/site/api_reference/core/entity/index.html @@ -1,4 +1,4 @@ - Entity - PixelRoot32 Documentation

Entity

Abstract base class for all game objects.

Description

Entity is the fundamental building block of the scene. Entities have a position, size, and lifecycle methods (update, draw). All game objects inherit from Entity, including actors, UI elements, and custom game objects.

Entities are managed by Scene and are automatically updated and drawn each frame when enabled and visible.

Namespace

namespace pixelroot32::core {
+ Entity - PixelRoot32 Documentation      

Entity

Abstract base class for all game objects.

Description

Entity is the fundamental building block of the scene. Entities have a position, size, and lifecycle methods (update, draw). All game objects inherit from Entity, including actors, UI elements, and custom game objects.

Entities are managed by Scene and are automatically updated and drawn each frame when enabled and visible.

Namespace

namespace pixelroot32::core {
     class Entity {
         // ...
     };
@@ -84,4 +84,4 @@
 private:
     float rotation = 0.0f;
 };
-

Performance Considerations

  • Visibility: Use isVisible = false instead of removing entities when hiding
  • Enable state: Use isEnabled = false to pause entity logic
  • Render layers: Organize entities by layer to minimize layer switches
  • Direct access: Direct property access is fast (no function call overhead)

ESP32 Considerations

  • Memory: Each entity consumes memory; stay within MAX_ENTITIES limit
  • Object pooling: Reuse entities instead of creating/destroying frequently
  • Update frequency: Disable entities that don't need to update every frame

See Also

\ No newline at end of file +

Performance Considerations

  • Visibility: Use isVisible = false instead of removing entities when hiding
  • Enable state: Use isEnabled = false to pause entity logic
  • Render layers: Organize entities by layer to minimize layer switches
  • Direct access: Direct property access is fast (no function call overhead)

ESP32 Considerations

  • Memory: Each entity consumes memory; stay within MAX_ENTITIES limit
  • Object pooling: Reuse entities instead of creating/destroying frequently
  • Update frequency: Disable entities that don't need to update every frame

See Also

\ No newline at end of file diff --git a/site/api_reference/core/input_config/index.html b/site/api_reference/core/input_config/index.html index 9b62d19..2b55018 100644 --- a/site/api_reference/core/input_config/index.html +++ b/site/api_reference/core/input_config/index.html @@ -1,4 +1,4 @@ - InputConfig - PixelRoot32 Documentation

InputConfig

Configuration structure for the InputManager.

Description

InputConfig defines the mapping between logical inputs and physical pins (ESP32) or keyboard keys (Native/SDL2). It uses variadic arguments to allow flexible configuration of any number of inputs.

The configuration is platform-specific: ESP32 uses GPIO pin numbers, while Native uses SDL keyboard scancodes.

Namespace

namespace pixelroot32::input {
+ InputConfig - PixelRoot32 Documentation      

InputConfig

Configuration structure for the InputManager.

Description

InputConfig defines the mapping between logical inputs and physical pins (ESP32) or keyboard keys (Native/SDL2). It uses variadic arguments to allow flexible configuration of any number of inputs.

The configuration is platform-specific: ESP32 uses GPIO pin numbers, while Native uses SDL keyboard scancodes.

Namespace

namespace pixelroot32::input {
     struct InputConfig {
         // ...
     };
@@ -117,4 +117,4 @@
     SDL_SCANCODE_SPACE,    // Jump
     SDL_SCANCODE_RETURN    // Action
 );
-

Performance Considerations

  • Memory: Arrays are allocated dynamically (small overhead)
  • Configuration: Done once at startup, no runtime cost
  • Access: Button indices are fast (array access)

ESP32 Considerations

  • Pin configuration: Ensure pins are not used by other peripherals
  • Debouncing: Hardware debouncing recommended for reliable input
  • Power: Buttons should use pull-up resistors to avoid floating pins

See Also

\ No newline at end of file +

Performance Considerations

  • Memory: Arrays are allocated dynamically (small overhead)
  • Configuration: Done once at startup, no runtime cost
  • Access: Button indices are fast (array access)

ESP32 Considerations

  • Pin configuration: Ensure pins are not used by other peripherals
  • Debouncing: Hardware debouncing recommended for reliable input
  • Power: Buttons should use pull-up resistors to avoid floating pins

See Also

\ No newline at end of file diff --git a/site/api_reference/core/input_manager/index.html b/site/api_reference/core/input_manager/index.html index a92dd95..06d233c 100644 --- a/site/api_reference/core/input_manager/index.html +++ b/site/api_reference/core/input_manager/index.html @@ -1,4 +1,4 @@ - InputManager - PixelRoot32 Documentation

InputManager

Handles input from physical buttons or keyboard (on PC).

Description

The InputManager polls configured pins (ESP32) or keyboard state (Native), handles debouncing, and tracks button states (Pressed, Released, Down, Clicked). It provides a unified input interface for both platforms.

The manager supports edge detection (just pressed/released) and continuous state (held down), making it suitable for both gameplay and UI navigation.

Namespace

namespace pixelroot32::input {
+ InputManager - PixelRoot32 Documentation      

InputManager

Handles input from physical buttons or keyboard (on PC).

Description

The InputManager polls configured pins (ESP32) or keyboard state (Native), handles debouncing, and tracks button states (Pressed, Released, Down, Clicked). It provides a unified input interface for both platforms.

The manager supports edge detection (just pressed/released) and continuous state (held down), making it suitable for both gameplay and UI navigation.

Namespace

namespace pixelroot32::input {
     class InputManager {
         // ...
     };
@@ -126,4 +126,4 @@
         }
     }
 };
-

Input State Comparison

Method Returns true when Use Case
isButtonPressed() Button just pressed this frame One-time actions (jump, shoot)
isButtonReleased() Button just released this frame Release events (stop charging)
isButtonClicked() Button pressed then released UI buttons, menu selection
isButtonDown() Button currently held Continuous actions (movement)

Performance Considerations

  • Update frequency: update() must be called every frame
  • Debouncing: Handled automatically, no performance impact
  • State queries: All query methods are fast (inline accessors)
  • Memory: Button state arrays are small and efficient

ESP32 Considerations

  • GPIO pins: Configure pins in InputConfig
  • Pull-up/pull-down: Ensure proper resistor configuration
  • Debouncing: Hardware debouncing recommended for noisy buttons
  • Pin limits: Some ESP32 pins have restrictions (check datasheet)

Native Considerations

  • Keyboard mapping: Uses SDL scancodes
  • Key detection: Automatically handles keyboard state
  • Multiple keys: Can detect multiple keys simultaneously

See Also

\ No newline at end of file +

Input State Comparison

Method Returns true when Use Case
isButtonPressed() Button just pressed this frame One-time actions (jump, shoot)
isButtonReleased() Button just released this frame Release events (stop charging)
isButtonClicked() Button pressed then released UI buttons, menu selection
isButtonDown() Button currently held Continuous actions (movement)

Performance Considerations

  • Update frequency: update() must be called every frame
  • Debouncing: Handled automatically, no performance impact
  • State queries: All query methods are fast (inline accessors)
  • Memory: Button state arrays are small and efficient

ESP32 Considerations

  • GPIO pins: Configure pins in InputConfig
  • Pull-up/pull-down: Ensure proper resistor configuration
  • Debouncing: Hardware debouncing recommended for noisy buttons
  • Pin limits: Some ESP32 pins have restrictions (check datasheet)

Native Considerations

  • Keyboard mapping: Uses SDL scancodes
  • Key detection: Automatically handles keyboard state
  • Multiple keys: Can detect multiple keys simultaneously

See Also

\ No newline at end of file diff --git a/site/api_reference/core/physics_actor/index.html b/site/api_reference/core/physics_actor/index.html index 8a7f322..5c89ff9 100644 --- a/site/api_reference/core/physics_actor/index.html +++ b/site/api_reference/core/physics_actor/index.html @@ -1,4 +1,4 @@ - PhysicsActor - PixelRoot32 Documentation

PhysicsActor

An actor with basic 2D physics properties.

Description

PhysicsActor extends the base Actor class by adding velocity, acceleration, friction, restitution (bounciness), and world boundary collision resolution. It is designed for objects that need to move and bounce within a defined area, such as balls, projectiles, or platformer characters.

PhysicsActor automatically handles: - Velocity-based movement - Friction application - World boundary collision and bouncing - Collision callbacks

Namespace

namespace pixelroot32::core {
+ PhysicsActor - PixelRoot32 Documentation      

PhysicsActor

An actor with basic 2D physics properties.

Description

PhysicsActor extends the base Actor class by adding velocity, acceleration, friction, restitution (bounciness), and world boundary collision resolution. It is designed for objects that need to move and bounce within a defined area, such as balls, projectiles, or platformer characters.

PhysicsActor automatically handles: - Velocity-based movement - Friction application - World boundary collision and bouncing - Collision callbacks

Namespace

namespace pixelroot32::core {
     class PhysicsActor : public Actor {
         // ...
     };
@@ -154,4 +154,4 @@
         playBounceSound();
     }
 };
-

Performance Considerations

  • Physics integration: Very efficient (simple velocity integration)
  • World bounds: Boundary checks are fast (AABB)
  • Friction: Applied every frame; keep friction values reasonable
  • Collision callbacks: Keep onCollision() and onWorldCollision() fast

ESP32 Considerations

  • Floating point: Uses float math; acceptable for ESP32 but integer math would be faster
  • Frame rate: Physics is frame-rate independent (uses deltaTime)
  • Memory: Each PhysicsActor consumes more memory than Actor (velocity, limits, etc.)

See Also

\ No newline at end of file +

Performance Considerations

  • Physics integration: Very efficient (simple velocity integration)
  • World bounds: Boundary checks are fast (AABB)
  • Friction: Applied every frame; keep friction values reasonable
  • Collision callbacks: Keep onCollision() and onWorldCollision() fast

ESP32 Considerations

  • Floating point: Uses float math; acceptable for ESP32 but integer math would be faster
  • Frame rate: Physics is frame-rate independent (uses deltaTime)
  • Memory: Each PhysicsActor consumes more memory than Actor (velocity, limits, etc.)

See Also

\ No newline at end of file diff --git a/site/api_reference/core/scene/index.html b/site/api_reference/core/scene/index.html index 104076b..e235e23 100644 --- a/site/api_reference/core/scene/index.html +++ b/site/api_reference/core/scene/index.html @@ -1,4 +1,4 @@ - Scene - PixelRoot32 Documentation

Scene

Represents a game level or screen containing entities.

Description

A Scene manages a collection of Entities and a CollisionSystem. It is responsible for updating and drawing all entities it contains. Scenes provide lifecycle hooks (init(), update(), draw()) to manage gameplay segments.

Scenes are the primary organizational unit in PixelRoot32, similar to levels or screens in other game engines. Each scene can contain up to MAX_ENTITIES (32) entities.

Namespace

namespace pixelroot32::core {
+ Scene - PixelRoot32 Documentation      

Scene

Represents a game level or screen containing entities.

Description

A Scene manages a collection of Entities and a CollisionSystem. It is responsible for updating and drawing all entities it contains. Scenes provide lifecycle hooks (init(), update(), draw()) to manage gameplay segments.

Scenes are the primary organizational unit in PixelRoot32, similar to levels or screens in other game engines. Each scene can contain up to MAX_ENTITIES (default 32; overridable via compiler flags) entities, and drawing uses up to MAX_LAYERS (default 3; overridable) render layers.

Namespace

namespace pixelroot32::core {
     class Scene {
         // ...
     };
@@ -57,7 +57,7 @@
     // Draw UI overlay
     renderer.drawText("Score: 100", 10, 10, Color::White, 1);
 }
-

void addEntity(Entity* entity)

Adds an entity to the scene.

Parameters: - entity (Entity*): Pointer to the Entity to add. Must not be nullptr.

Notes: - Entities are added to an internal queue - Maximum of MAX_ENTITIES (32) entities per scene - If the limit is reached, the entity may not be added (check return value if available) - Entities are updated and drawn in the order they were added - The entity's lifetime is managed by the scene (do not delete manually while in scene)

Example:

void init() override {
+

void addEntity(Entity* entity)

Adds an entity to the scene.

Parameters: - entity (Entity*): Pointer to the Entity to add. Must not be nullptr.

Notes: - Entities are added to an internal queue - Maximum of MAX_ENTITIES (default 32; overridable) entities per scene - If the limit is reached, the entity may not be added (check return value if available) - Entities are updated and drawn in the order they were added - The entity's lifetime is managed by the scene (do not delete manually while in scene)

Example:

void init() override {
     // Create and add player
     PlayerActor* player = new PlayerActor();
     player->setPosition(64, 64);
@@ -80,49 +80,52 @@
         entityPool.returnToPool(entity);
     }
 }
-

Protected Members

ArduinoQueue entities

Queue of entities in the scene. Accessible to derived classes for custom entity management.

Type: ArduinoQueue<Entity*>

Notes: - Maximum capacity: MAX_ENTITIES (32) - Direct access allows custom iteration or filtering - Use with caution: modifying while iterating may cause issues

CollisionSystem collisionSystem

System to handle collisions between actors. Accessible to derived classes for custom collision handling.

Type: pixelroot32::physics::CollisionSystem

Notes: - Automatically processes collisions between actors - Uses collision layers and masks for filtering - Can be accessed for custom collision queries

MAX_ENTITIES Constant

The maximum number of entities allowed per scene.

Value: 32

Notes: - Hard limit: cannot be changed without modifying engine code - Includes all entity types: actors, UI elements, particles, etc. - Plan your entity usage carefully - Use object pooling to reuse entities instead of creating new ones

Usage Example

#include "core/Scene.h"
-#include "core/Actor.h"
-
-class MyGameScene : public pixelroot32::core::Scene {
-private:
-    PlayerActor* player;
-
-public:
-    void init() override {
-        // Create player
-        player = new PlayerActor();
-        player->setPosition(64, 64);
-        addEntity(player);
-
-        // Create some enemies
-        for (int i = 0; i < 5; i++) {
-            EnemyActor* enemy = new EnemyActor();
-            enemy->setPosition(10 + i * 20, 10);
-            addEntity(enemy);
-        }
-    }
-
-    void update(unsigned long deltaTime) override {
-        // Custom game logic
-        if (player->isDead()) {
-            // Handle game over
-        }
-
-        // Update entities and collisions
-        Scene::update(deltaTime);
-    }
-
-    void draw(pixelroot32::graphics::Renderer& renderer) override {
-        // Draw background
-        renderer.drawFilledRectangle(0, 0, 128, 128, Color::Black);
-
-        // Draw all entities
-        Scene::draw(renderer);
-
-        // Draw HUD
-        char scoreText[32];
-        snprintf(scoreText, sizeof(scoreText), "Score: %d", score);
-        renderer.drawText(scoreText, 10, 10, Color::White, 1);
-    }
-};
-

Performance Considerations

  • Entity limit: MAX_ENTITIES = 32 is a hard limit; plan accordingly
  • Add/Remove: Frequent add/remove operations can be expensive; use object pooling
  • Update order: Entities are updated in add order; consider order for dependencies
  • Collision checks: CollisionSystem automatically handles actor collisions efficiently

ESP32 Considerations

  • Memory: Each entity consumes memory; stay well below the limit
  • Object pooling: Essential for ESP32 to avoid memory fragmentation
  • Scene switching: Clearing and recreating scenes can fragment memory; reuse scenes when possible

See Also

\ No newline at end of file +

Protected Members

ArduinoQueue entities

Queue of entities in the scene. Accessible to derived classes for custom entity management.

Type: ArduinoQueue<Entity*>

Notes: - Maximum capacity: MAX_ENTITIES (default 32; overridable) - Direct access allows custom iteration or filtering - Use with caution: modifying while iterating may cause issues

CollisionSystem collisionSystem

System to handle collisions between actors. Accessible to derived classes for custom collision handling.

Type: pixelroot32::physics::CollisionSystem

Notes: - Automatically processes collisions between actors - Uses collision layers and masks for filtering - Can be accessed for custom collision queries

Overriding scene limits (MAX_LAYERS / MAX_ENTITIES)

The engine defines default limits in core/Scene.h: MAX_LAYERS (default 3) and MAX_ENTITIES (default 32). These are guarded with #ifndef, so you can override them from your project without modifying the engine.

ESP32 platform limitation

The default of 3 for MAX_LAYERS is due to ESP32 platform constraints (memory and draw-loop cost). On native/PC you can safely use a higher value; on ESP32, increasing it may affect performance or memory.

In your project (e.g. in platformio.ini), add the defines to build_flags for the environment you use:

build_flags =
+    -DMAX_LAYERS=5
+    -DMAX_ENTITIES=64
+

The compiler defines MAX_LAYERS and MAX_ENTITIES before processing any .cpp file. Because Scene.h uses #ifndef MAX_LAYERS / #ifndef MAX_ENTITIES, it will not redefine them and your values will be used.

Effect: - MAX_LAYERS: Number of render layers drawn in Scene::draw() (layer 0 = background, 1+ = sprite context). Increasing this allows more distinct draw layers (e.g. background, platforms, gameplay, foreground, UI). - MAX_ENTITIES: On Arduino, the capacity of the scene entity queue when constructed with this value. On native (mock queue), the value is ignored (unbounded).

See also: Platforms and Drivers - Scene limits.

Usage Example

#include "core/Scene.h"
+#include "core/Actor.h"
+
+class MyGameScene : public pixelroot32::core::Scene {
+private:
+    PlayerActor* player;
+
+public:
+    void init() override {
+        // Create player
+        player = new PlayerActor();
+        player->setPosition(64, 64);
+        addEntity(player);
+
+        // Create some enemies
+        for (int i = 0; i < 5; i++) {
+            EnemyActor* enemy = new EnemyActor();
+            enemy->setPosition(10 + i * 20, 10);
+            addEntity(enemy);
+        }
+    }
+
+    void update(unsigned long deltaTime) override {
+        // Custom game logic
+        if (player->isDead()) {
+            // Handle game over
+        }
+
+        // Update entities and collisions
+        Scene::update(deltaTime);
+    }
+
+    void draw(pixelroot32::graphics::Renderer& renderer) override {
+        // Draw background
+        renderer.drawFilledRectangle(0, 0, 128, 128, Color::Black);
+
+        // Draw all entities
+        Scene::draw(renderer);
+
+        // Draw HUD
+        char scoreText[32];
+        snprintf(scoreText, sizeof(scoreText), "Score: %d", score);
+        renderer.drawText(scoreText, 10, 10, Color::White, 1);
+    }
+};
+

Performance Considerations

  • Entity limit: MAX_ENTITIES (default 32) can be overridden via compiler flags; plan accordingly
  • Add/Remove: Frequent add/remove operations can be expensive; use object pooling
  • Update order: Entities are updated in add order; consider order for dependencies
  • Collision checks: CollisionSystem automatically handles actor collisions efficiently

ESP32 Considerations

  • Memory: Each entity consumes memory; stay well below the limit
  • Object pooling: Essential for ESP32 to avoid memory fragmentation
  • Scene switching: Clearing and recreating scenes can fragment memory; reuse scenes when possible

See Also

\ No newline at end of file diff --git a/site/api_reference/graphics/camera2d/index.html b/site/api_reference/graphics/camera2d/index.html index 44bac14..3f4b1e3 100644 --- a/site/api_reference/graphics/camera2d/index.html +++ b/site/api_reference/graphics/camera2d/index.html @@ -1,4 +1,4 @@ - Camera2D - PixelRoot32 Documentation

Camera2D

2D camera for scrolling and viewport control.

Description

Camera2D controls viewport position and enables scrolling by shifting the renderer's display offset. It supports following targets, boundary constraints, and can be used for parallax effects.

The camera uses a dead-zone system: it only moves when the target is outside a central zone, creating smooth following behavior.

Namespace

namespace pixelroot32::graphics {
+ Camera2D - PixelRoot32 Documentation      

Camera2D

2D camera for scrolling and viewport control.

Description

Camera2D controls viewport position and enables scrolling by shifting the renderer's display offset. It supports following targets, boundary constraints, and can be used for parallax effects.

The camera uses a dead-zone system: it only moves when the target is outside a central zone, creating smooth following behavior.

Namespace

namespace pixelroot32::graphics {
     class Camera2D {
         // ...
     };
@@ -120,4 +120,4 @@
     renderer.setDisplayOffset(0, 0);
     renderer.drawText("Score: 100", 10, 10, Color::White, 1);
 }
-

Performance Considerations

  • Apply frequency: apply() is fast; safe to call every frame
  • Boundary checks: Boundary clamping is efficient
  • Following: Dead-zone calculations are lightweight

ESP32 Considerations

  • Float math: Uses floating point; acceptable but integer math would be faster
  • Memory: Camera is small (few floats); minimal memory usage

See Also

\ No newline at end of file +

Performance Considerations

  • Apply frequency: apply() is fast; safe to call every frame
  • Boundary checks: Boundary clamping is efficient
  • Following: Dead-zone calculations are lightweight

ESP32 Considerations

  • Float math: Uses floating point; acceptable but integer math would be faster
  • Memory: Camera is small (few floats); minimal memory usage

See Also

\ No newline at end of file diff --git a/site/api_reference/graphics/color/index.html b/site/api_reference/graphics/color/index.html index 579fa79..99fd85c 100644 --- a/site/api_reference/graphics/color/index.html +++ b/site/api_reference/graphics/color/index.html @@ -1,4 +1,4 @@ - Color - PixelRoot32 Documentation

Color

Color constants and palette management system.

Description

The Color enum provides color constants that map to palette indices. The engine supports both legacy mode (single global palette) and dual palette mode (separate palettes for backgrounds and sprites).

Colors are resolved to 16-bit RGB565 values based on the active palette(s).

Namespace

namespace pixelroot32::graphics {
+ Color - PixelRoot32 Documentation      

Color

Color constants and palette management system.

Description

The Color enum provides color constants that map to palette indices. The engine supports both legacy mode (single global palette) and dual palette mode (separate palettes for backgrounds and sprites).

Colors are resolved to 16-bit RGB565 values based on the active palette(s).

Namespace

namespace pixelroot32::graphics {
     enum class Color : uint8_t {
         // ...
     };
@@ -70,4 +70,4 @@
 pixelroot32::graphics::enableDualPaletteMode(true);
 pixelroot32::graphics::setBackgroundCustomPalette(CUSTOM_PALETTE);
 pixelroot32::graphics::setSpriteCustomPalette(CUSTOM_PALETTE);
-

Performance Considerations

  • Color resolution: Fast lookup operation
  • Palette switching: Changing palettes is fast (just pointer assignment)
  • Memory: Palettes are stored in flash (const arrays) for best performance
  • Dual mode: Slightly more overhead than legacy mode, but minimal

ESP32 Considerations

  • Flash storage: Store custom palettes in flash (const/constexpr)
  • Memory: Palettes are small (16 uint16_t = 32 bytes)
  • Palette switching: Avoid switching palettes every frame

See Also

\ No newline at end of file +

Performance Considerations

  • Color resolution: Fast lookup operation
  • Palette switching: Changing palettes is fast (just pointer assignment)
  • Memory: Palettes are stored in flash (const arrays) for best performance
  • Dual mode: Slightly more overhead than legacy mode, but minimal

ESP32 Considerations

  • Flash storage: Store custom palettes in flash (const/constexpr)
  • Memory: Palettes are small (16 uint16_t = 32 bytes)
  • Palette switching: Avoid switching palettes every frame

See Also

\ No newline at end of file diff --git a/site/api_reference/graphics/display_config/index.html b/site/api_reference/graphics/display_config/index.html index 2df057c..2bf3117 100644 --- a/site/api_reference/graphics/display_config/index.html +++ b/site/api_reference/graphics/display_config/index.html @@ -1,4 +1,4 @@ - DisplayConfig - PixelRoot32 Documentation

DisplayConfig

Configuration settings for initializing the display.

Description

DisplayConfig holds display parameters used by the renderer and camera to draw correctly on the target device. It defines the display type, dimensions, rotation, and creates the appropriate DrawSurface implementation for the platform.

Namespace

namespace pixelroot32::graphics {
+ DisplayConfig - PixelRoot32 Documentation      

DisplayConfig

Configuration settings for initializing the display.

Description

DisplayConfig holds display parameters used by the renderer and camera to draw correctly on the target device. It defines the display type, dimensions, rotation, and creates the appropriate DrawSurface implementation for the platform.

Namespace

namespace pixelroot32::graphics {
     struct DisplayConfig {
         // ...
     };
@@ -87,4 +87,4 @@
     -DTFT_WIDTH=240
     -DTFT_HEIGHT=240
     # ... pin configuration
-

Pin Configuration

GPIO pins must be configured separately (not in DisplayConfig):

  • MOSI: Data pin
  • SCLK: Clock pin
  • DC: Data/Command pin
  • RST: Reset pin
  • CS: Chip select pin (optional)

See Also

\ No newline at end of file +

Pin Configuration

GPIO pins must be configured separately (not in DisplayConfig):

  • MOSI: Data pin
  • SCLK: Clock pin
  • DC: Data/Command pin
  • RST: Reset pin
  • CS: Chip select pin (optional)

See Also

\ No newline at end of file diff --git a/site/api_reference/graphics/font/index.html b/site/api_reference/graphics/font/index.html index 05a256b..06f4278 100644 --- a/site/api_reference/graphics/font/index.html +++ b/site/api_reference/graphics/font/index.html @@ -1,4 +1,4 @@ - Font - PixelRoot32 Documentation

Font

Descriptor for a bitmap font using 1bpp sprites.

Description

A Font contains an array of Sprite structures, one for each character in the font's character set. Each glyph is rendered as a 1bpp sprite, allowing consistent rendering across platforms.

The font uses fixed-width glyphs for simplicity and performance. All glyphs share the same width and height, with spacing between characters controlled by the spacing field.

Namespace

namespace pixelroot32::graphics {
+ Font - PixelRoot32 Documentation      

Font

Descriptor for a bitmap font using 1bpp sprites.

Description

A Font contains an array of Sprite structures, one for each character in the font's character set. Each glyph is rendered as a 1bpp sprite, allowing consistent rendering across platforms.

The font uses fixed-width glyphs for simplicity and performance. All glyphs share the same width and height, with spacing between characters controlled by the spacing field.

Namespace

namespace pixelroot32::graphics {
     struct Font {
         // ...
     };
@@ -121,4 +121,4 @@
 int getTextHeight(const Font* font) {
     return font ? font->lineHeight : 8;
 }
-

Performance Considerations

  • Font storage: Store fonts in flash (const/constexpr) for best performance
  • Glyph lookup: Fast array access (character code - firstChar)
  • Fixed width: Fixed-width fonts are faster than variable-width
  • Font switching: Changing fonts is fast (just pointer assignment)

ESP32 Considerations

  • Memory: Store font data in flash, not RAM
  • Font size: Larger fonts use more flash memory
  • Character range: Limit character range to save memory if not needed

See Also

\ No newline at end of file +

Performance Considerations

  • Font storage: Store fonts in flash (const/constexpr) for best performance
  • Glyph lookup: Fast array access (character code - firstChar)
  • Fixed width: Fixed-width fonts are faster than variable-width
  • Font switching: Changing fonts is fast (just pointer assignment)

ESP32 Considerations

  • Memory: Store font data in flash, not RAM
  • Font size: Larger fonts use more flash memory
  • Character range: Limit character range to save memory if not needed

See Also

\ No newline at end of file diff --git a/site/api_reference/graphics/renderer/index.html b/site/api_reference/graphics/renderer/index.html index 457c822..63be19d 100644 --- a/site/api_reference/graphics/renderer/index.html +++ b/site/api_reference/graphics/renderer/index.html @@ -1,4 +1,4 @@ - Renderer - PixelRoot32 Documentation

Renderer

High-level graphics rendering system for drawing shapes, text, sprites, and tilemaps.

Description

The Renderer class provides a unified API for drawing shapes, text, and images. It abstracts the underlying hardware implementation (DrawSurface) and manages display configuration, including rotation and offsets.

The renderer uses integer-only math for optimal performance on ESP32 and supports multiple sprite formats (1bpp, 2bpp, 4bpp) and multi-layer sprites.

Namespace

namespace pixelroot32::graphics {
+ Renderer - PixelRoot32 Documentation      

Renderer

High-level graphics rendering system for drawing shapes, text, sprites, and tilemaps.

Description

The Renderer class provides a unified API for drawing shapes, text, and images. It abstracts the underlying hardware implementation (DrawSurface) and manages display configuration, including rotation and offsets.

The renderer uses integer-only math for optimal performance on ESP32 and supports multiple sprite formats (1bpp, 2bpp, 4bpp) and multi-layer sprites.

Namespace

namespace pixelroot32::graphics {
     class Renderer {
         // ...
     };
@@ -100,4 +100,4 @@
 
     renderer.endFrame();
 }
-

Performance Considerations

  • Integer-only math: All operations use integer arithmetic for ESP32 efficiency
  • Sprite storage: Store sprite data in flash (const/constexpr) for best performance
  • Batch operations: Group similar draw calls together
  • Tilemaps: Use tilemaps for backgrounds instead of individual sprites
  • Viewport culling: Only draw what's visible on screen

ESP32 Considerations

  • Memory: Sprite data should be in flash, not RAM
  • Frame rate: Limit draw calls per frame for consistent FPS
  • Display offset: Use for scrolling instead of redrawing everything

See Also

\ No newline at end of file +

Performance Considerations

  • Integer-only math: All operations use integer arithmetic for ESP32 efficiency
  • Sprite storage: Store sprite data in flash (const/constexpr) for best performance
  • Batch operations: Group similar draw calls together
  • Tilemaps: Dibuja un mapa de tiles completo. Implementa viewport culling automático y caché de paleta para máximo rendimiento.
  • Sprites 2bpp/4bpp: Optimizado para ESP32 (IRAM + acceso de 16 bits).

ESP32 Considerations

  • Memory: Sprite data should be in flash, not RAM
  • Frame rate: Limit draw calls per frame for consistent FPS
  • Display offset: Use for scrolling instead of redrawing everything

See Also

\ No newline at end of file diff --git a/site/api_reference/graphics/sprite/index.html b/site/api_reference/graphics/sprite/index.html index 63e8759..ccd770b 100644 --- a/site/api_reference/graphics/sprite/index.html +++ b/site/api_reference/graphics/sprite/index.html @@ -1,4 +1,4 @@ - Sprite - PixelRoot32 Documentation

Sprite

Low-level bitmap descriptor and multi-layer composition for retro rendering.

Description

Sprites are the fundamental graphics primitive in PixelRoot32. The engine supports multiple sprite formats:

  • 1bpp (Standard): Monochrome sprites, most memory-efficient
  • 2bpp (Experimental): 4 colors per sprite
  • 4bpp (Experimental): 16 colors per sprite
  • MultiSprite: Multi-layer 1bpp sprites for multi-color effects

Namespace

namespace pixelroot32::graphics {
+ Sprite - PixelRoot32 Documentation      

Sprite

Low-level bitmap descriptor and multi-layer composition for retro rendering.

Description

Sprites are the fundamental graphics primitive in PixelRoot32. The engine supports multiple sprite formats:

  • 1bpp (Standard): Monochrome sprites, most memory-efficient
  • 2bpp (Experimental): 4 colors per sprite
  • 4bpp (Experimental): 16 colors per sprite
  • MultiSprite: Multi-layer 1bpp sprites for multi-color effects

Namespace

namespace pixelroot32::graphics {
     struct Sprite {
         // ...
     };
@@ -55,7 +55,7 @@
     LAYERS, // layers array
     2       // layer count
 };
-

Sprite2bpp Structure (Experimental)

2-bit per pixel sprite (4 colors).

Requires: PIXELROOT32_ENABLE_2BPP_SPRITES build flag

Members

  • const uint8_t* data: Packed 2bpp data
  • const Color* palette: Local palette (4 colors)
  • uint8_t width: Sprite width
  • uint8_t height: Sprite height
  • uint8_t paletteSize: Number of colors (typically 4)

Notes

  • Experimental feature
  • Uses more memory than 1bpp
  • Each pixel can be one of 4 colors from local palette

Sprite4bpp Structure (Experimental)

4-bit per pixel sprite (16 colors).

Requires: PIXELROOT32_ENABLE_4BPP_SPRITES build flag

Members

  • const uint8_t* data: Packed 4bpp data
  • const Color* palette: Local palette (16 colors)
  • uint8_t width: Sprite width
  • uint8_t height: Sprite height
  • uint8_t paletteSize: Number of colors (typically 16)

Notes

  • Experimental feature
  • Uses more memory than 1bpp/2bpp
  • Each pixel can be one of 16 colors from local palette

Sprite Animation

SpriteAnimationFrame Structure

Frame that can reference either a Sprite or a MultiSprite.

Members: - const Sprite* sprite: Optional pointer to a simple 1bpp sprite frame - const MultiSprite* multiSprite: Optional pointer to a layered sprite frame

Notes: - Exactly one pointer should be non-null for a valid frame - Allows same animation system for both sprite types

SpriteAnimation Structure

Lightweight, step-based sprite animation controller.

Members: - const SpriteAnimationFrame* frames: Pointer to immutable frame table - uint8_t frameCount: Number of frames in the table - uint8_t current: Current frame index [0, frameCount)

Methods: - void reset(): Reset to first frame - void step(): Advance to next frame (wrapping) - const SpriteAnimationFrame& getCurrentFrame() const: Get current frame - const Sprite* getCurrentSprite() const: Get current simple sprite - const MultiSprite* getCurrentMultiSprite() const: Get current multi-sprite

Example:

static const SpriteAnimationFrame WALK_FRAMES[] = {
+

Sprite2bpp Structure (Experimental)

2-bit per pixel sprite (4 colors).

Requires: PIXELROOT32_ENABLE_2BPP_SPRITES build flag

Members

  • const uint16_t* data: Datos empaquetados (4 píxeles por cada 8 bits, alineados a 16 bits)
  • const Color* palette: Local palette (4 colors)
  • uint8_t width: Sprite width
  • uint8_t height: Sprite height
  • uint8_t paletteSize: Number of colors (typically 4)

Notes

  • Experimental feature
  • Uses more memory than 1bpp
  • Each pixel can be one of 4 colors from local palette

Sprite4bpp Structure (Experimental)

4-bit per pixel sprite (16 colors).

Requires: PIXELROOT32_ENABLE_4BPP_SPRITES build flag

Members

  • const uint16_t* data: Datos empaquetados (2 píxeles por cada 8 bits, alineados a 16 bits)
  • const Color* palette: Local palette (16 colors)
  • uint8_t width: Sprite width
  • uint8_t height: Sprite height
  • uint8_t paletteSize: Number of colors (typically 16)

Notes

  • Experimental feature
  • Uses more memory than 1bpp/2bpp
  • Each pixel can be one of 16 colors from local palette

Sprite Animation

SpriteAnimationFrame Structure

Frame that can reference either a Sprite or a MultiSprite.

Members: - const Sprite* sprite: Optional pointer to a simple 1bpp sprite frame - const MultiSprite* multiSprite: Optional pointer to a layered sprite frame

Notes: - Exactly one pointer should be non-null for a valid frame - Allows same animation system for both sprite types

SpriteAnimation Structure

Lightweight, step-based sprite animation controller.

Members: - const SpriteAnimationFrame* frames: Pointer to immutable frame table - uint8_t frameCount: Number of frames in the table - uint8_t current: Current frame index [0, frameCount)

Methods: - void reset(): Reset to first frame - void step(): Advance to next frame (wrapping) - const SpriteAnimationFrame& getCurrentFrame() const: Get current frame - const Sprite* getCurrentSprite() const: Get current simple sprite - const MultiSprite* getCurrentMultiSprite() const: Get current multi-sprite

Example:

static const SpriteAnimationFrame WALK_FRAMES[] = {
     {&walkFrame1, nullptr},
     {&walkFrame2, nullptr},
     {&walkFrame3, nullptr},
@@ -153,4 +153,4 @@
 
 // Draw flipped
 renderer.drawSprite(sprite, 100, 100, Color::White, true);
-

Performance Considerations

  • 1bpp sprites: Most efficient (integer-only operations)
  • MultiSprite: Each layer is a separate draw call (still efficient)
  • 2bpp/4bpp: Experimental, uses more memory and CPU
  • Storage: Store sprite data in flash (const/constexpr) for best performance
  • Size limit: Sprites are limited to 16 pixels wide for 1bpp format

ESP32 Considerations

  • Memory: Store sprite data in flash, not RAM
  • Sprite size: Smaller sprites = faster drawing
  • Format choice: Use 1bpp when possible for best performance
  • MultiSprite: More layers = more draw calls (but acceptable)

See Also

\ No newline at end of file +

Performance Considerations

  • 1bpp sprites: Most efficient (integer-only operations)
  • MultiSprite: Each layer is a separate draw call (still efficient)
  • 2bpp/4bpp: Experimental, uses more memory and CPU
  • Storage: Store sprite data in flash (const/constexpr) for best performance
  • Size limit: Sprites are limited to 16 pixels wide for 1bpp format

ESP32 Considerations

  • Memory: Store sprite data in flash, not RAM
  • Sprite size: Smaller sprites = faster drawing
  • Format choice: Use 1bpp when possible for best performance
  • MultiSprite: More layers = more draw calls (but acceptable)

See Also

\ No newline at end of file diff --git a/site/api_reference/graphics/tilemap/index.html b/site/api_reference/graphics/tilemap/index.html index ff408af..74c483e 100644 --- a/site/api_reference/graphics/tilemap/index.html +++ b/site/api_reference/graphics/tilemap/index.html @@ -1,4 +1,4 @@ - TileMap - PixelRoot32 Documentation

TileMap

Generic structure for tile-based background rendering.

Description

TileMapGeneric<T> is a template structure for rendering tile-based backgrounds efficiently. It supports multiple bit-depths (1bpp, 2bpp, 4bpp) by using the appropriate sprite type for tiles.

Tilemaps are ideal for large backgrounds, levels, and static environments. They support viewport culling (only visible tiles are drawn) for optimal performance.

Namespace

namespace pixelroot32::graphics {
+ TileMap - PixelRoot32 Documentation      

TileMap

Generic structure for tile-based background rendering.

Description

TileMapGeneric<T> is a template structure for rendering tile-based backgrounds efficiently. It supports multiple bit-depths (1bpp, 2bpp, 4bpp) by using the appropriate sprite type for tiles.

Tilemaps are ideal for large backgrounds, levels, and static environments. They support viewport culling (only visible tiles are drawn) for optimal performance.

Namespace

namespace pixelroot32::graphics {
     template<typename T>
     struct TileMapGeneric {
         // ...
@@ -182,4 +182,4 @@
         return (tile == 1);  // Wall tile
     }
 };
-

Performance Considerations

  • Viewport culling: Only visible tiles are drawn (automatic)
  • Tile reuse: Reuse tile sprites across the map
  • Index storage: Compact uint8_t indices (1 byte per tile)
  • Memory: Store indices and tiles in flash (const) for best performance
  • Tile size: Smaller tiles = more tiles to draw, but more detail

ESP32 Considerations

  • Memory: Store tilemap data in flash, not RAM
  • Map size: Large maps use more flash memory
  • Tile count: Limit unique tiles to save memory
  • Culling: Viewport culling is essential for large levels

See Also

\ No newline at end of file +

Performance Considerations

  • Viewport culling: Only visible tiles are drawn (automatic)
  • Tile reuse: Reuse tile sprites across the map
  • Index storage: Compact uint8_t indices (1 byte per tile)
  • Memory: Store indices and tiles in flash (const) for best performance
  • Tile size: Smaller tiles = more tiles to draw, but more detail

ESP32 Considerations

  • Memory: Store tilemap data in flash, not RAM
  • Map size: Large maps use more flash memory
  • Tile count: Limit unique tiles to save memory
  • Culling: Viewport culling is essential for large levels

See Also

\ No newline at end of file diff --git a/site/api_reference/physics/collision_system/index.html b/site/api_reference/physics/collision_system/index.html index 7df780e..b9ed24c 100644 --- a/site/api_reference/physics/collision_system/index.html +++ b/site/api_reference/physics/collision_system/index.html @@ -1,4 +1,4 @@ - CollisionSystem - PixelRoot32 Documentation

CollisionSystem

Manages collision detection between entities.

Description

CollisionSystem iterates through registered entities, checks if they are Actors, and performs AABB (Axis-Aligned Bounding Box) collision checks based on their collision layers and masks.

The system automatically filters collisions using layers and masks, avoiding unnecessary checks. When a collision is detected, it triggers the onCollision() callback on both actors.

Namespace

namespace pixelroot32::physics {
+ CollisionSystem - PixelRoot32 Documentation      

CollisionSystem

Manages collision detection between entities.

Description

CollisionSystem iterates through registered entities, checks if they are Actors, and performs AABB (Axis-Aligned Bounding Box) collision checks based on their collision layers and masks.

The system automatically filters collisions using layers and masks, avoiding unnecessary checks. When a collision is detected, it triggers the onCollision() callback on both actors.

Namespace

namespace pixelroot32::physics {
     class CollisionSystem {
         // ...
     };
@@ -59,4 +59,4 @@
         Scene::update(deltaTime);
     }
 };
-

Performance Considerations

  • Layer filtering: Very efficient; avoids most collision checks
  • AABB checks: Fast (simple rectangle intersection)
  • Pair checking: O(n²) complexity, but n is limited (MAX_ENTITIES = 32)
  • Update frequency: Called every frame; keep hitboxes simple

ESP32 Considerations

  • Entity limit: MAX_ENTITIES = 32 limits collision pairs
  • Layer efficiency: Use layers effectively to minimize checks
  • Hitbox simplicity: Keep hitboxes as simple AABB for best performance

See Also

\ No newline at end of file +

Performance Considerations

  • Layer filtering: Very efficient; avoids most collision checks
  • AABB checks: Fast (simple rectangle intersection)
  • Pair checking: O(n²) complexity, but n is limited (MAX_ENTITIES = 32)
  • Update frequency: Called every frame; keep hitboxes simple

ESP32 Considerations

  • Entity limit: MAX_ENTITIES = 32 limits collision pairs
  • Layer efficiency: Use layers effectively to minimize checks
  • Hitbox simplicity: Keep hitboxes as simple AABB for best performance

See Also

\ No newline at end of file diff --git a/site/api_reference/physics/collision_types/index.html b/site/api_reference/physics/collision_types/index.html index 3f530da..c821306 100644 --- a/site/api_reference/physics/collision_types/index.html +++ b/site/api_reference/physics/collision_types/index.html @@ -1,4 +1,4 @@ - Collision Types - PixelRoot32 Documentation

Collision Types

Basic geometric types and collision layer definitions.

Description

This document describes the collision primitives (Rect, Circle, Segment) and collision layer system used by the collision detection system.

Namespace

namespace pixelroot32::physics {
+ Collision Types - PixelRoot32 Documentation      

Collision Types

Basic geometric types and collision layer definitions.

Description

This document describes the collision primitives (Rect, Circle, Segment) and collision layer system used by the collision detection system.

Namespace

namespace pixelroot32::physics {
     // Types and functions
 }
 

CollisionLayer Type

Collision layer type (bit flags).

Type: uint16_t (typedef)

Notes: - Uses bit flags for layer assignment - Actors can be on multiple layers (bitwise OR) - Masks define which layers an actor can collide with

DefaultLayers Namespace

Predefined collision layers.

kNone

No layer (0).

Value: 0

Usage:

actor->layer = pixelroot32::physics::DefaultLayers::kNone;
@@ -99,4 +99,4 @@
         return false;
     }
 };
-

Performance Considerations

  • AABB checks: Very fast (simple rectangle intersection)
  • Circle checks: Slightly slower (distance calculation)
  • Sweep tests: More expensive (use only for fast-moving objects)
  • Layer filtering: Essential for performance with many actors

ESP32 Considerations

  • Float math: Uses floating point; acceptable but integer math would be faster
  • Sweep tests: Use sparingly (more CPU intensive)
  • Layer efficiency: Use layers effectively to minimize checks

See Also

\ No newline at end of file +

Performance Considerations

  • AABB checks: Very fast (simple rectangle intersection)
  • Circle checks: Slightly slower (distance calculation)
  • Sweep tests: More expensive (use only for fast-moving objects)
  • Layer filtering: Essential for performance with many actors

ESP32 Considerations

  • Float math: Uses floating point; acceptable but integer math would be faster
  • Sweep tests: Use sparingly (more CPU intensive)
  • Layer efficiency: Use layers effectively to minimize checks

See Also

\ No newline at end of file diff --git a/site/api_reference/ui/ui_button/index.html b/site/api_reference/ui/ui_button/index.html index 53f212b..bb32124 100644 --- a/site/api_reference/ui/ui_button/index.html +++ b/site/api_reference/ui/ui_button/index.html @@ -1,4 +1,4 @@ - UIButton - PixelRoot32 Documentation

UIButton

A clickable button UI element.

Description

UIButton is a clickable button that supports both physical (keyboard/gamepad) and touch input. It can trigger a callback function when pressed and integrates with UI layouts for automatic navigation.

Buttons support selection state (for D-pad navigation), custom styling, and text alignment.

Namespace

namespace pixelroot32::graphics::ui {
+ UIButton - PixelRoot32 Documentation      

UIButton

A clickable button UI element.

Description

UIButton is a clickable button that supports both physical (keyboard/gamepad) and touch input. It can trigger a callback function when pressed and integrates with UI layouts for automatic navigation.

Buttons support selection state (for D-pad navigation), custom styling, and text alignment.

Namespace

namespace pixelroot32::graphics::ui {
     class UIButton : public UIElement {
         // ...
     };
@@ -131,4 +131,4 @@
 // D-pad navigation is automatic
 // UP/DOWN moves selection
 // Action button (A) triggers selected button
-

Performance Considerations

  • Input handling: handleInput() is fast; safe to call every frame
  • Rendering: Simple rectangle and text; very efficient
  • Memory: Each button consumes memory (stay within MAX_ENTITIES)

ESP32 Considerations

  • String storage: Button labels use std::string; consider memory usage
  • Callback functions: Use function pointers or lambdas (both efficient)

See Also

\ No newline at end of file +

Performance Considerations

  • Input handling: handleInput() is fast; safe to call every frame
  • Rendering: Simple rectangle and text; very efficient
  • Memory: Each button consumes memory (stay within MAX_ENTITIES)

ESP32 Considerations

  • String storage: Button labels use std::string; consider memory usage
  • Callback functions: Use function pointers or lambdas (both efficient)

See Also

\ No newline at end of file diff --git a/site/api_reference/ui/ui_checkbox/index.html b/site/api_reference/ui/ui_checkbox/index.html index 8d0f3c1..3865323 100644 --- a/site/api_reference/ui/ui_checkbox/index.html +++ b/site/api_reference/ui/ui_checkbox/index.html @@ -1,4 +1,4 @@ - UICheckBox - PixelRoot32 Documentation

UICheckBox

A clickable checkbox UI element.

Description

UICheckBox is a clickable checkbox that can be toggled between checked and unchecked states. It supports both physical (keyboard/gamepad) and touch input. It can trigger a callback function when its state changes and integrates with UI layouts for automatic navigation.

Namespace

namespace pixelroot32::graphics::ui {
+ UICheckBox - PixelRoot32 Documentation      

UICheckBox

A clickable checkbox UI element.

Description

UICheckBox is a clickable checkbox that can be toggled between checked and unchecked states. It supports both physical (keyboard/gamepad) and touch input. It can trigger a callback function when its state changes and integrates with UI layouts for automatic navigation.

Namespace

namespace pixelroot32::graphics::ui {
     class UICheckBox : public UIElement {
         // ...
     };
@@ -26,4 +26,4 @@
 

Public Methods

void setStyle(Color textCol, Color bgCol, bool drawBg = false)

Configures the checkbox's visual style.

Parameters: - textCol (Color): Color of the text - bgCol (Color): Color of the background - drawBg (bool, optional): Whether to draw the background rectangle. Default: false

Returns: - void

void setChecked(bool checked)

Sets the checked state.

Parameters: - checked (bool): True if checked

Returns: - void

bool isChecked() const

Checks if the checkbox is currently checked.

Returns: - bool: true if checked

void toggle()

Toggles the checkbox state and triggers the callback.

Returns: - void

void setSelected(bool selected)

Sets the selection state (e.g., focused via D-pad).

Parameters: - selected (bool): True if selected

Returns: - void

bool getSelected() const

Checks if the checkbox is currently selected.

Returns: - bool: true if selected

Callbacks

onCheckChanged

The onCheckChanged callback is a std::function<void(bool)> that is triggered whenever the checkbox state changes via setChecked() or toggle().

checkbox->onCheckChanged = [](bool isChecked) {
     Serial.println(isChecked ? "Checked!" : "Unchecked!");
 };
-

UICheckBox is designed to work seamlessly with UILayout containers (like UIVerticalLayout).

  • Focusable: Returns true for isFocusable(), allowing it to receive focus in a layout.
  • Input Handling: When selected (focused), it listens for the button index provided in the constructor (typically the 'A' button) to toggle its state.
  • Visual Feedback: When selected, it displays a selection indicator (usually a > character) if no background is drawn, or highlights its text/border.
\ No newline at end of file +

UICheckBox is designed to work seamlessly with UILayout containers (like UIVerticalLayout).

  • Focusable: Returns true for isFocusable(), allowing it to receive focus in a layout.
  • Input Handling: When selected (focused), it listens for the button index provided in the constructor (typically the 'A' button) to toggle its state.
  • Visual Feedback: When selected, it displays a selection indicator (usually a > character) if no background is drawn, or highlights its text/border.
\ No newline at end of file diff --git a/site/api_reference/ui/ui_element/index.html b/site/api_reference/ui/ui_element/index.html index dddaf6c..c262b80 100644 --- a/site/api_reference/ui/ui_element/index.html +++ b/site/api_reference/ui/ui_element/index.html @@ -1,4 +1,4 @@ - UIElement - PixelRoot32 Documentation

UIElement

Base class for all user interface elements.

Description

UIElement is the base class for all UI components (buttons, labels, panels, etc.). It inherits from Entity to integrate with the scene graph and automatically sets the entity type to UI_ELEMENT and render layer to 2 (UI layer).

Namespace

namespace pixelroot32::graphics::ui {
+ UIElement - PixelRoot32 Documentation      

UIElement

Base class for all user interface elements.

Description

UIElement is the base class for all UI components (buttons, labels, panels, etc.). It inherits from Entity to integrate with the scene graph and automatically sets the entity type to UI_ELEMENT and render layer to 2 (UI layer).

Namespace

namespace pixelroot32::graphics::ui {
     enum class UIElementType {
         GENERIC,
         BUTTON,
@@ -72,4 +72,4 @@
         h = static_cast<float>(height);
     }
 };
-

Performance Considerations

  • Render layer: UI elements are on layer 2, drawn after gameplay
  • Visibility: Use isVisible = false to hide elements efficiently
  • Layout integration: Layouts automatically manage element positioning

ESP32 Considerations

  • Memory: Each UI element consumes memory (stay within MAX_ENTITIES)
  • Object pooling: Reuse UI elements when possible
  • Update frequency: Disable UI elements that don't need to update

See Also

\ No newline at end of file +

Performance Considerations

  • Render layer: UI elements are on layer 2, drawn after gameplay
  • Visibility: Use isVisible = false to hide elements efficiently
  • Layout integration: Layouts automatically manage element positioning

ESP32 Considerations

  • Memory: Each UI element consumes memory (stay within MAX_ENTITIES)
  • Object pooling: Reuse UI elements when possible
  • Update frequency: Disable UI elements that don't need to update

See Also

\ No newline at end of file diff --git a/site/api_reference/ui/ui_label/index.html b/site/api_reference/ui/ui_label/index.html index d2c04ee..1c3c3e0 100644 --- a/site/api_reference/ui/ui_label/index.html +++ b/site/api_reference/ui/ui_label/index.html @@ -1,4 +1,4 @@ - UILabel - PixelRoot32 Documentation

UILabel

A simple text label UI element.

Description

UILabel displays a string of text on the screen. It auto-calculates its bounds based on text length and font size, making it easy to create dynamic text displays.

Labels are useful for displaying scores, status messages, menu text, and other UI information.

Namespace

namespace pixelroot32::graphics::ui {
+ UILabel - PixelRoot32 Documentation      

UILabel

A simple text label UI element.

Description

UILabel displays a string of text on the screen. It auto-calculates its bounds based on text length and font size, making it easy to create dynamic text displays.

Labels are useful for displaying scores, status messages, menu text, and other UI information.

Namespace

namespace pixelroot32::graphics::ui {
     class UILabel : public UIElement {
         // ...
     };
@@ -96,4 +96,4 @@
 );
 title->centerX(128);  // Center on screen
 addEntity(title);
-

Performance Considerations

  • Text updates: setText() recalculates size; avoid calling every frame if text doesn't change
  • String storage: Uses std::string; consider memory on ESP32
  • Rendering: Simple text drawing; very efficient
  • Static text: For static text, create once and don't update

ESP32 Considerations

  • Memory: std::string uses heap memory; use static buffers when possible
  • Text updates: Limit frequency of text updates
  • String length: Keep text short to save memory

See Also

\ No newline at end of file +

Performance Considerations

  • Text updates: setText() recalculates size; avoid calling every frame if text doesn't change
  • String storage: Uses std::string; consider memory on ESP32
  • Rendering: Simple text drawing; very efficient
  • Static text: For static text, create once and don't update

ESP32 Considerations

  • Memory: std::string uses heap memory; use static buffers when possible
  • Text updates: Limit frequency of text updates
  • String length: Keep text short to save memory

See Also

\ No newline at end of file diff --git a/site/api_reference/ui/ui_layout/index.html b/site/api_reference/ui/ui_layout/index.html index a929694..b740a6c 100644 --- a/site/api_reference/ui/ui_layout/index.html +++ b/site/api_reference/ui/ui_layout/index.html @@ -1,6 +1,6 @@ - UILayout - PixelRoot32 Documentation

UILayout

Base class for UI layout containers.

Description

UILayout is the base class for all layout containers. Layouts organize UI elements automatically, handling positioning, spacing, and optional scrolling. Layouts are themselves UI elements that can be added to scenes.

Namespace

namespace pixelroot32::graphics::ui {
+ UILayout - PixelRoot32 Documentation      

UILayout

Base class for UI layout containers.

Description

UILayout is the base class for all layout containers. Layouts organize UI elements automatically, handling positioning, spacing, and optional scrolling. Layouts are themselves UI elements that can be added to scenes.

Namespace

namespace pixelroot32::graphics::ui {
     class UILayout : public UIElement {
         // ...
     };
 }
-

Inheritance

  • Inherits from: UIElement
  • Inherited by: UIVerticalLayout, UIHorizontalLayout, UIGridLayout, UIAnchorLayout

ScrollBehavior Enum

Defines how scrolling behaves in layouts.

Values: - ScrollBehavior::NONE: No scrolling allowed - ScrollBehavior::SCROLL: Scroll freely within bounds - ScrollBehavior::CLAMP: Scroll but clamp to content bounds

Public Methods

virtual void addElement(UIElement* element) = 0

Adds a UI element to the layout. Must be implemented by derived classes.

Parameters: - element (UIElement*): Pointer to the element to add

virtual void removeElement(UIElement* element) = 0

Removes a UI element from the layout. Must be implemented by derived classes.

Parameters: - element (UIElement*): Pointer to the element to remove

virtual void updateLayout() = 0

Recalculates positions of all elements in the layout. Must be implemented by derived classes.

Returns: - void

Notes: - Should be called automatically when elements are added/removed

virtual void handleInput(const InputManager& input) = 0

Handles input for layout navigation (scroll, selection, etc.). Must be implemented by derived classes.

Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

void setPadding(float p)

Sets the padding (internal spacing) of the layout.

Parameters: - p (float): Padding value in pixels

Returns: - void

Notes: - Layout is automatically recalculated

float getPadding() const

Gets the current padding.

Returns: - float: Padding value in pixels

void setSpacing(float s)

Sets the spacing between elements.

Parameters: - s (float): Spacing value in pixels

Returns: - void

Notes: - Layout is automatically recalculated - Default: 4.0 pixels

float getSpacing() const

Gets the current spacing.

Returns: - float: Spacing value in pixels

size_t getElementCount() const

Gets the number of elements in the layout.

Returns: - size_t: Element count

UIElement* getElement(size_t index) const

Gets the element at a specific index.

Parameters: - index (size_t): Element index

Returns: - UIElement*: Pointer to the element, or nullptr if index is invalid

void clearElements()

Clears all elements from the layout.

Returns: - void

Notes: - Elements are not deleted (you must manage their lifetimes) - Layout is automatically recalculated

Protected Members

  • std::vector<UIElement*> elements: List of child elements
  • float padding: Internal padding
  • float spacing: Spacing between elements (default: 4.0)
  • float scrollOffset: Current scroll offset
  • bool enableScroll: Whether scrolling is enabled
  • ScrollBehavior scrollBehavior: Scroll behavior mode

See Also

\ No newline at end of file +

Inheritance

  • Inherits from: UIElement
  • Inherited by: UIVerticalLayout, UIHorizontalLayout, UIGridLayout, UIAnchorLayout

ScrollBehavior Enum

Defines how scrolling behaves in layouts.

Values: - ScrollBehavior::NONE: No scrolling allowed - ScrollBehavior::SCROLL: Scroll freely within bounds - ScrollBehavior::CLAMP: Scroll but clamp to content bounds

Public Methods

virtual void addElement(UIElement* element) = 0

Adds a UI element to the layout. Must be implemented by derived classes.

Parameters: - element (UIElement*): Pointer to the element to add

virtual void removeElement(UIElement* element) = 0

Removes a UI element from the layout. Must be implemented by derived classes.

Parameters: - element (UIElement*): Pointer to the element to remove

virtual void updateLayout() = 0

Recalculates positions of all elements in the layout. Must be implemented by derived classes.

Returns: - void

Notes: - Should be called automatically when elements are added/removed

virtual void handleInput(const InputManager& input) = 0

Handles input for layout navigation (scroll, selection, etc.). Must be implemented by derived classes.

Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

void setPadding(float p)

Sets the padding (internal spacing) of the layout.

Parameters: - p (float): Padding value in pixels

Returns: - void

Notes: - Layout is automatically recalculated

float getPadding() const

Gets the current padding.

Returns: - float: Padding value in pixels

void setSpacing(float s)

Sets the spacing between elements.

Parameters: - s (float): Spacing value in pixels

Returns: - void

Notes: - Layout is automatically recalculated - Default: 4.0 pixels

float getSpacing() const

Gets the current spacing.

Returns: - float: Spacing value in pixels

size_t getElementCount() const

Gets the number of elements in the layout.

Returns: - size_t: Element count

UIElement* getElement(size_t index) const

Gets the element at a specific index.

Parameters: - index (size_t): Element index

Returns: - UIElement*: Pointer to the element, or nullptr if index is invalid

void clearElements()

Clears all elements from the layout.

Returns: - void

Notes: - Elements are not deleted (you must manage their lifetimes) - Layout is automatically recalculated

Protected Members

  • std::vector<UIElement*> elements: List of child elements
  • float padding: Internal padding
  • float spacing: Spacing between elements (default: 4.0)
  • float scrollOffset: Current scroll offset
  • bool enableScroll: Whether scrolling is enabled
  • ScrollBehavior scrollBehavior: Scroll behavior mode

See Also

\ No newline at end of file diff --git a/site/api_reference/ui/ui_layouts/anchor_layout/index.html b/site/api_reference/ui/ui_layouts/anchor_layout/index.html index 65616ce..9eed77e 100644 --- a/site/api_reference/ui/ui_layouts/anchor_layout/index.html +++ b/site/api_reference/ui/ui_layouts/anchor_layout/index.html @@ -1,4 +1,4 @@ - Anchor Layout - PixelRoot32 Documentation

UIAnchorLayout

Layout that positions elements at fixed anchor points on the screen.

Description

UIAnchorLayout positions UI elements at fixed anchor points (corners, center, edges) without reflow. Very efficient for HUDs, debug UI, and fixed-position elements. Positions are calculated once or when screen size changes.

This layout is ideal for HUD elements like score, lives, health bars, and other fixed-position UI.

Namespace

namespace pixelroot32::graphics::ui {
+ Anchor Layout - PixelRoot32 Documentation      

UIAnchorLayout

Layout that positions elements at fixed anchor points on the screen.

Description

UIAnchorLayout positions UI elements at fixed anchor points (corners, center, edges) without reflow. Very efficient for HUDs, debug UI, and fixed-position elements. Positions are calculated once or when screen size changes.

This layout is ideal for HUD elements like score, lives, health bars, and other fixed-position UI.

Namespace

namespace pixelroot32::graphics::ui {
     class UIAnchorLayout : public UILayout {
         // ...
     };
@@ -89,4 +89,4 @@
         // HUD is drawn automatically (on layer 2)
     }
 };
-

Anchor Positioning

Elements are positioned based on their anchor:

  • TOP_LEFT: Element's top-left at screen top-left
  • TOP_RIGHT: Element's top-right at screen top-right
  • BOTTOM_LEFT: Element's bottom-left at screen bottom-left
  • BOTTOM_RIGHT: Element's bottom-right at screen bottom-right
  • CENTER: Element centered on screen
  • TOP_CENTER: Element centered horizontally, top-aligned
  • BOTTOM_CENTER: Element centered horizontally, bottom-aligned
  • LEFT_CENTER: Element centered vertically, left-aligned
  • RIGHT_CENTER: Element centered vertically, right-aligned

Performance Considerations

  • No reflow: Very efficient (positions calculated once)
  • Fixed positions: Ideal for HUD elements
  • Viewport independent: Elements stay in fixed screen positions

ESP32 Considerations

  • Memory: Very efficient (no complex calculations)
  • Update frequency: Positions only recalculate when screen size changes

See Also

\ No newline at end of file +

Anchor Positioning

Elements are positioned based on their anchor:

  • TOP_LEFT: Element's top-left at screen top-left
  • TOP_RIGHT: Element's top-right at screen top-right
  • BOTTOM_LEFT: Element's bottom-left at screen bottom-left
  • BOTTOM_RIGHT: Element's bottom-right at screen bottom-right
  • CENTER: Element centered on screen
  • TOP_CENTER: Element centered horizontally, top-aligned
  • BOTTOM_CENTER: Element centered horizontally, bottom-aligned
  • LEFT_CENTER: Element centered vertically, left-aligned
  • RIGHT_CENTER: Element centered vertically, right-aligned

Performance Considerations

  • No reflow: Very efficient (positions calculated once)
  • Fixed positions: Ideal for HUD elements
  • Viewport independent: Elements stay in fixed screen positions

ESP32 Considerations

  • Memory: Very efficient (no complex calculations)
  • Update frequency: Positions only recalculate when screen size changes

See Also

\ No newline at end of file diff --git a/site/api_reference/ui/ui_layouts/grid_layout/index.html b/site/api_reference/ui/ui_layouts/grid_layout/index.html index 84449e8..dbd9ae0 100644 --- a/site/api_reference/ui/ui_layouts/grid_layout/index.html +++ b/site/api_reference/ui/ui_layouts/grid_layout/index.html @@ -1,4 +1,4 @@ - Grid Layout - PixelRoot32 Documentation

UIGridLayout

Grid layout container for organizing elements in a matrix.

Description

UIGridLayout organizes UI elements in a fixed grid of rows and columns. It supports navigation in 4 directions (UP/DOWN/LEFT/RIGHT) and automatic positioning based on grid coordinates.

This layout is ideal for inventories, level selection screens, galleries, and any grid-based UI arrangement.

Namespace

namespace pixelroot32::graphics::ui {
+ Grid Layout - PixelRoot32 Documentation      

UIGridLayout

Grid layout container for organizing elements in a matrix.

Description

UIGridLayout organizes UI elements in a fixed grid of rows and columns. It supports navigation in 4 directions (UP/DOWN/LEFT/RIGHT) and automatic positioning based on grid coordinates.

This layout is ideal for inventories, level selection screens, galleries, and any grid-based UI arrangement.

Namespace

namespace pixelroot32::graphics::ui {
     class UIGridLayout : public UILayout {
         // ...
     };
@@ -39,4 +39,4 @@
         addEntity(inventory);
     }
 };
-

See Also

\ No newline at end of file +

See Also

\ No newline at end of file diff --git a/site/api_reference/ui/ui_layouts/horizontal_layout/index.html b/site/api_reference/ui/ui_layouts/horizontal_layout/index.html index 4ff24ce..13b2f68 100644 --- a/site/api_reference/ui/ui_layouts/horizontal_layout/index.html +++ b/site/api_reference/ui/ui_layouts/horizontal_layout/index.html @@ -1,4 +1,4 @@ - Horizontal Layout - PixelRoot32 Documentation

UIHorizontalLayout

Horizontal layout container with scroll support.

Description

UIHorizontalLayout organizes UI elements horizontally, one next to another. It supports scrolling when content exceeds the visible viewport and handles keyboard/D-pad navigation automatically.

This layout is ideal for toolbars, tab bars, horizontal menus, and any horizontal arrangement of UI elements.

Namespace

namespace pixelroot32::graphics::ui {
+ Horizontal Layout - PixelRoot32 Documentation      

UIHorizontalLayout

Horizontal layout container with scroll support.

Description

UIHorizontalLayout organizes UI elements horizontally, one next to another. It supports scrolling when content exceeds the visible viewport and handles keyboard/D-pad navigation automatically.

This layout is ideal for toolbars, tab bars, horizontal menus, and any horizontal arrangement of UI elements.

Namespace

namespace pixelroot32::graphics::ui {
     class UIHorizontalLayout : public UILayout {
         // ...
     };
@@ -35,4 +35,4 @@
         addEntity(toolbar);
     }
 };
-

See Also

\ No newline at end of file +

See Also

\ No newline at end of file diff --git a/site/api_reference/ui/ui_layouts/padding_container/index.html b/site/api_reference/ui/ui_layouts/padding_container/index.html index a9214df..6903341 100644 --- a/site/api_reference/ui/ui_layouts/padding_container/index.html +++ b/site/api_reference/ui/ui_layouts/padding_container/index.html @@ -1,4 +1,4 @@ - Padding Container - PixelRoot32 Documentation

UIPaddingContainer

Container that wraps a single UI element and applies padding.

Description

UIPaddingContainer adds padding/margin around a single child element without organizing multiple elements. Useful for adding spacing to individual elements or nesting layouts with custom padding.

This container is simpler than UIPanel (no background/border) and focuses only on spacing.

Namespace

namespace pixelroot32::graphics::ui {
+ Padding Container - PixelRoot32 Documentation      

UIPaddingContainer

Container that wraps a single UI element and applies padding.

Description

UIPaddingContainer adds padding/margin around a single child element without organizing multiple elements. Useful for adding spacing to individual elements or nesting layouts with custom padding.

This container is simpler than UIPanel (no background/border) and focuses only on spacing.

Namespace

namespace pixelroot32::graphics::ui {
     class UIPaddingContainer : public UIElement {
         // ...
     };
@@ -42,4 +42,4 @@
 auto* paddedLayout = new UIPaddingContainer(0, 0, 128, 128);
 paddedLayout->setPadding(10.0f, 10.0f, 20.0f, 20.0f);  // Asymmetric
 paddedLayout->setChild(layout);
-

Performance Considerations

  • Rendering: Very efficient (just draws child)
  • Position calculation: Fast (simple addition)
  • Memory: Minimal overhead

ESP32 Considerations

  • Memory: Very lightweight
  • Update frequency: Position only recalculates when padding/position changes

See Also

\ No newline at end of file +

Performance Considerations

  • Rendering: Very efficient (just draws child)
  • Position calculation: Fast (simple addition)
  • Memory: Minimal overhead

ESP32 Considerations

  • Memory: Very lightweight
  • Update frequency: Position only recalculates when padding/position changes

See Also

\ No newline at end of file diff --git a/site/api_reference/ui/ui_layouts/panel/index.html b/site/api_reference/ui/ui_layouts/panel/index.html index e9ee5e5..117206b 100644 --- a/site/api_reference/ui/ui_layouts/panel/index.html +++ b/site/api_reference/ui/ui_layouts/panel/index.html @@ -1,4 +1,4 @@ - Panel - PixelRoot32 Documentation

UIPanel

Visual container that draws a background and border around a child element.

Description

UIPanel provides a retro-style window/panel appearance with a background color and border. Typically contains a UILayout or other UI elements. Useful for dialogs, menus, and information panels.

The panel wraps a single child element and draws a background rectangle and border around it.

Namespace

namespace pixelroot32::graphics::ui {
+ Panel - PixelRoot32 Documentation      

UIPanel

Visual container that draws a background and border around a child element.

Description

UIPanel provides a retro-style window/panel appearance with a background color and border. Typically contains a UILayout or other UI elements. Useful for dialogs, menus, and information panels.

The panel wraps a single child element and draws a background rectangle and border around it.

Namespace

namespace pixelroot32::graphics::ui {
     class UIPanel : public UIElement {
         // ...
     };
@@ -64,4 +64,4 @@
         addEntity(dialog);
     }
 };
-

Performance Considerations

  • Rendering: Simple rectangles; very efficient
  • Child updates: Child element updates are fast
  • Memory: Small overhead (just colors and border width)

ESP32 Considerations

  • Memory: Panel is lightweight
  • Rendering: Two rectangles (background + border); minimal overhead

See Also

\ No newline at end of file +

Performance Considerations

  • Rendering: Simple rectangles; very efficient
  • Child updates: Child element updates are fast
  • Memory: Small overhead (just colors and border width)

ESP32 Considerations

  • Memory: Panel is lightweight
  • Rendering: Two rectangles (background + border); minimal overhead

See Also

\ No newline at end of file diff --git a/site/api_reference/ui/ui_layouts/vertical_layout/index.html b/site/api_reference/ui/ui_layouts/vertical_layout/index.html index 2b694c5..fc67bdf 100644 --- a/site/api_reference/ui/ui_layouts/vertical_layout/index.html +++ b/site/api_reference/ui/ui_layouts/vertical_layout/index.html @@ -1,4 +1,4 @@ - Vertical Layout - PixelRoot32 Documentation

UIVerticalLayout

Vertical layout container with scroll support.

Description

UIVerticalLayout organizes UI elements vertically, one below another. It supports scrolling when content exceeds the visible viewport and handles keyboard/D-pad navigation automatically.

This layout is ideal for menus, lists, and any vertical arrangement of UI elements.

Namespace

namespace pixelroot32::graphics::ui {
+ Vertical Layout - PixelRoot32 Documentation      

UIVerticalLayout

Vertical layout container with scroll support.

Description

UIVerticalLayout organizes UI elements vertically, one below another. It supports scrolling when content exceeds the visible viewport and handles keyboard/D-pad navigation automatically.

This layout is ideal for menus, lists, and any vertical arrangement of UI elements.

Namespace

namespace pixelroot32::graphics::ui {
     class UIVerticalLayout : public UILayout {
         // ...
     };
@@ -80,4 +80,4 @@
         Scene::draw(renderer);  // Draws layout and buttons
     }
 };
-

The layout handles D-pad navigation automatically:

  • UP button: Moves selection up
  • DOWN button: Moves selection down
  • Action button: Triggers selected button's callback
  • Scrolling: Automatically scrolls to keep selected element visible

Performance Considerations

  • Viewport culling: Only visible elements are drawn
  • Layout recalculation: Fast (simple positioning)
  • Scrolling: Smooth scrolling is efficient

ESP32 Considerations

  • Element count: Stay within MAX_ENTITIES limit
  • Scrolling: Smooth scrolling uses minimal CPU

See Also

\ No newline at end of file +

The layout handles D-pad navigation automatically:

  • UP button: Moves selection up
  • DOWN button: Moves selection down
  • Action button: Triggers selected button's callback
  • Scrolling: Automatically scrolls to keep selected element visible

Performance Considerations

  • Viewport culling: Only visible elements are drawn
  • Layout recalculation: Fast (simple positioning)
  • Scrolling: Smooth scrolling is efficient

ESP32 Considerations

  • Element count: Stay within MAX_ENTITIES limit
  • Scrolling: Smooth scrolling uses minimal CPU

See Also

\ No newline at end of file diff --git a/site/assets/images/favicon.ico b/site/assets/images/favicon.ico index ef2c28dbe249eb4818f57e77be4f71a72ff5a3f3..1c193e2f0ad22a0558079526e4598b50fb832462 100644 GIT binary patch delta 863 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�k*)|{2*`T1#@pOHnn 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 diff --git a/site/assets/images/logo.png b/site/assets/images/logo.png index 3fc136adcea49629c52704f6f7b610eb1cc493c4..be17b9da1ad516cde839c3cd25690a431945818e 100644 GIT binary patch literal 4048 zcmaJ^X;c&0x@8JNm>L;ukl`|^42lX!1!NWj4p9ja0tkxGQ9&#Xpg?2@DozmWRuCyb zZ`8=FMxjyB1Qfjj2?~e-TM8M%potO$QHFOC^sV>j-C8Saozyw!`}W!2{&rQJ*-pS~ zU=6Wya&j8}ej5Vh1s?1E^v~9ufj{Jx(c-PV-^|vX zwmR()m2Z{1`afZ2EFW*bieSdZ9lF+8a)~V%KW2#IsC8`D)clV}uWfw`4&?WcQo9Gm z?6DF54gc-h`JZhiPm)ptL;W{cDr|+%o=zujP(sYjQC)3$)@c>UcLPV2psfuiCNkQ0 zF4p#J*s$K9KiK;ZedD|AQ4ua|~M2u-LaPcly zV`(R7<9sDfMwRFmgK1t-Nc_n-t*4w7YYh-e+fzGMQ`pOHxY*0(rAB_etFLmap5q$V zw3Tfk4b$Va`{=}uu&6yy#O*V)ekSKk0nod;NG%=U1lue5he6U)S=4)6Gx)3@74o@i zsLBhdEwnw{T=zCQS$H;9iK4ZBZ{K;G_ev+PxqZ8gFPsHuyb$D-@8BPxy08so zu}v$T$SDQ1fr^1jd(zp4h&6M%j)+{fCUU0FMa)hL-#woHoT)#WB~GKI{*ym%suo)r z^EtF(fBvN#eKTXdb8{8Z$;vj6uPn~DTU7~agbQc-W(KR%rf9v3zl|nDl!$4#C0N1* zg2gb3FrDqh9V`O;vAQg;3sh z{$06iRJNpnytqjJcDA}upiTKWmY34YHw>CVLWM>J5vz@2u60jCQeuZH0j|01a3Ful0(V7y=VwF0q~o<#2##4%eY7FB z`kRJf0`mOU8BrLD(X1ZbK zXy?)Urk$(o(LgESQgq9Y-?az^nnajRje?91znp->w}ij(M=QP}YxpIKtX;u3=@Kr< zv}7Xs=~RUkBRSbQUdbgP{`;#*@z?!kiG|m_v7p#=IFF-?^qIDQY5=A~mplSqA+-!t ziL%CzazZjUOZ;$Di3J)fl#|rkbCi@p

    a&^RgtjYD+Mz7vJGI@l6xy9t@HJyI=WC zt8q7DFbaZ=CDc3oJ35HdP)(xJ{lOX?8699<=AsqAJf;5<@ST~t>%DCT!aj8N`(zN3 zMEY=!yRj-F4VOiCm{h5EM(PyzW4H#>$}1IkGmOz2eHCAKxD8N1gum%j6$p=eUNHXc z3OxC~F6g9WHp?`%iu@}Pw;;u$A4p~|-y<)0l1a_O@#R5Yst;6x<@YO5-p60p#>#-8 zMy9-qW-lK#DfO@aY9;O{cau+zhA5*e;M8w(&nbTcw4jjk*ERw-fY%urDNqa`YGNcP z#j?ve_xcYG%8t}-jB@K}fV?m{-cHw~VM4vjBMVN_WbkRQUmJc?Y|d6h);tNVobnQl z$@4JES?2goX+(N^yd``pv%vMX6d`CiKtOYYDiIc`QL3Beh$vE7+be#|<{IFz_UOAs zyz(LwB-(X6g~jYuLdtURLF=Z#TgW@qti` z+qyq;%iioI&L2Awp07!O^U@uTpaIE@vMJBW;V(`mh17xB;UQytDVDmv^#B2NIkZ_9 zoScGz_9k9^+coy_NK0K4OE!t|=Zr@XsIm5rBi3UeHzeR`vC&a2`Q2DiWP z4ESW5l}ML3r%g9TOSck~C@uN-0JES!*U=@T?$?4e>iGLU@?|;^cHoincLRz@z|2cC zMfYi;2_pyL;JS>_%^Y&S=4aICSfE>!<1D0ANKPnN>K>0jc49~m3F80t{SM6v zGBq;@f$0uQc>gNlK}lH2=lEKH3BgrO}pL>uSZ=$L|)t>Q8PoVDG}8 zayVLKhJ%omLiZ|7p2iz)5SRhq&TS93He zqN&xbO2Szj@51}#IPu61Pq*Z3+fRje9T0n!rf}ThNvd`Gg%ALow3Dz|YcfNU>~^Ha z16q>K?r@IGac3-arz{as8aJ=d*>!xYA4l*TFThnD@;Er^c(VXCiXQUKKA6%&4tY{* zIJf6dJ@k>cR8}lzvsvHz?`al|f&*0_)ZxjQZz=>hPEBYKWzk5ESxz`!%Mt;DNyBI=4lG6T%AD)I&v_=W3E%ND@3R4EV<+XjYn97`4L5tajnb1pclGYRzT@6 z;T9Gm>E)tMNFkD^lqSfr=R2ypiu>D!d>w(HI_!kGK#U-Q2_%NM(k#KMz?+(S+&SD&3f z`3j9q60Cr@1~W!e!F1{&C?$Lh7{;%l(vPM!&TJv(;W7CD)^2&S_&5VNa8k2Pgeb-KtMkNh7l9qGbXYY%Ac z&Py%<7=SUhxwI%y&SZdBO==7jddMD_0Hy5)=7`%NBL!Yg`-R9MnGG0;^WxDhw7GZF ztX~q-&tgxw$RsCR3pcG&1JQ)$aFb{Y`caXxZ*wF0LmefZ{md%gtz;>3(t|KbXHPiI z*)|G3yjQtj^28ExdK_eopmsUlE&NQ$F18obQ2gvw-}$5n5VV~`{;@(A>2D-|pN$xe z+gslUjFlbd1j;{|&ejiS7Ev)E+xvcLDRq$MM)K&<;pvl!?1oYl(I=2PeSeCWP9*6e zWo4*RS((CO=^&7VVF8(wNI<|O@TBPfJ~PK2eiPi(c{kuq{g6xEyF9b8E9}Yx^^b2v z)S6T;k@?>LRgRQ5VGev#^!v&}c4qC<+5t^s^7B=7#+&R|j-F1b)t9@N1EvfUv4A_d zAFfiO*kDnbp=c`Z%Ob6x+5n1HrnDAOEuoa+yT8lxx`j~aqj2!p>^SbZZy6LLdW(jc z5;A>Iqs6>DU?@MEO-el_lRZdi*8!6wtZk8|4&CT)(DQBVzuIjZ$$LAx*7Fi!(OL!- zk8%WZR#I-j_teTEY>tvpB|mI3z1=Dw5X2)_6M^UMDi?)W{t7~8k%2w{OQbvb`u!iFmE-SqzfxqyYo literal 148597 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 {$LgJ%Eh7aw})NsLt?%S=Qm*&gQrN7UClkyH5 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 b?6F`$d7(R(Y}{)_pBTh$6UFGFrNw^(30LcT literal 44132 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 diff --git a/site/examples/space_invaders/overview/index.html b/site/examples/space_invaders/overview/index.html deleted file mode 100644 index 3c5fa4e..0000000 --- a/site/examples/space_invaders/overview/index.html +++ /dev/null @@ -1,229 +0,0 @@ - Overview - PixelRoot32 Documentation

    Space Invaders Example

    A complete implementation of the classic Space Invaders game showcasing advanced PixelRoot32 features including sprite animations, collision detection with sweep tests, dynamic music tempo, tilemap backgrounds, and efficient entity management.

    Overview

    This example demonstrates a fully playable Space Invaders game with: - Player ship with horizontal movement and shooting - Formation of 32 aliens (4 rows × 8 columns) with different types - Defensive bunkers that degrade when hit - Dynamic background music that speeds up as aliens approach - Visual explosion effects - Score and lives system - Win/lose conditions

    Architecture

    Scene Structure

    SpaceInvadersScene (SpaceInvadersScene.h/cpp) - Main game scene managing all entities and game state - Handles game loop, collisions, spawning, and state transitions - Implements custom entity management to work within MAX_ENTITIES limit

    Entity Classes

    PlayerActor

    • Type: PhysicsActor
    • Features:
    • Horizontal movement controlled by LEFT/RIGHT buttons
    • Shooting with FIRE button
    • Physics-based movement with world boundaries
    • Respawn system after being hit

    AlienActor

    • Type: Actor
    • Features:
    • Three types: SQUID (top row, 30 points), CRAB (middle rows, 20 points), OCTOPUS (bottom rows, 10 points)
    • Step-based sprite animations (alternates frames on movement)
    • Formation movement (moves as a group, drops down when hitting edges)
    • Uses MultiSprite for CRAB type (multi-layer sprite)

    ProjectileActor

    • Type: PhysicsActor
    • Features:
    • Two types: PLAYER_BULLET (white, moves up) and ENEMY_BULLET (red, moves down)
    • Object pooling (reuses projectiles instead of creating/destroying)
    • Sweep test collision detection for fast-moving projectiles
    • Tracks previous position for accurate collision detection

    BunkerActor

    • Type: Actor
    • Features:
    • Health system (starts at 4, decreases when hit)
    • Visual degradation (color changes: Green → Yellow → Red)
    • Collision detection with dynamic hitbox based on remaining health

    Background

    TilemapBackground - Starfield created using tilemap system - Procedurally generated pattern - Rendered on layer 0 (background)

    Key Systems

    Collision Detection

    The game uses sweep tests for accurate collision detection with fast-moving projectiles:

    // Example from SpaceInvadersScene::handleCollisions()
    -Circle startCircle;
    -startCircle.x = proj->getPreviousX() + radius;
    -startCircle.y = proj->getPreviousY() + PROJECTILE_HEIGHT * 0.5f;
    -startCircle.radius = radius;
    -
    -Circle endCircle;
    -endCircle.x = proj->x + radius;
    -endCircle.y = proj->y + PROJECTILE_HEIGHT * 0.5f;
    -endCircle.radius = radius;
    -
    -float tHit = 0.0f;
    -if (sweepCircleVsRect(startCircle, endCircle, targetBox, tHit)) {
    -    // Collision detected
    -}
    -

    Why sweep tests? - Projectiles move fast (120 px/s) - Standard AABB can miss collisions between frames - Sweep tests check the path between previous and current position

    Entity Management

    Due to the MAX_ENTITIES = 32 limit, the scene uses custom entity management:

    // Custom vectors instead of Scene's entity system
    -std::vector<AlienActor*> aliens;
    -std::vector<ProjectileActor*> projectiles;
    -std::vector<BunkerActor*> bunkers;
    -

    Object Pooling: - Projectiles are pre-allocated (MaxProjectiles = 12) - Projectiles are reused instead of created/destroyed - reset() method reactivates projectiles

    Scene Arena (Optional): - Uses PIXELROOT32_ENABLE_SCENE_ARENA if available - Pre-allocates memory for all entities - Avoids heap fragmentation

    Dynamic Music Tempo

    The background music tempo increases as aliens get closer to the player:

    void SpaceInvadersScene::updateMusicTempo() {
    -    // Find lowest active alien
    -    float lowestY = /* ... */;
    -
    -    // Calculate threat factor (0.0 to 1.0)
    -    float threatFactor = (lowestY - ALIEN_START_Y) * INV_Y_RANGE;
    -
    -    // Target tempo: 1.0 (slow) to 1.9 (fast)
    -    float targetTempo = 1.0f + (threatFactor * 0.9f);
    -
    -    // Smooth interpolation
    -    currentMusicTempoFactor += (targetTempo - currentMusicTempoFactor) * 0.05f;
    -
    -    engine.getMusicPlayer().setTempoFactor(currentMusicTempoFactor);
    -}
    -

    Music Tracks: - BGM_SLOW_TRACK: Initial tempo (slow) - BGM_MEDIUM_TRACK: Medium tempo (unused, can be used for transitions) - BGM_FAST_TRACK: Fast tempo (unused, can be used for transitions) - WIN_TRACK: Victory music (plays once) - GAME_OVER_TRACK: Defeat music (plays once)

    Visual Effects

    Enemy Explosions

    • Simple cross pattern (horizontal + vertical lines)
    • Pool of 8 explosion effects
    • 200ms duration each
    • Reused slots to avoid allocations

    Player Explosion

    • 3-frame sprite animation
    • Plays when player is hit
    • Pauses gameplay during animation
    • Respawns player after animation completes

    Alien Formation Movement

    Aliens move in lockstep formation:

    void SpaceInvadersScene::updateAliens(unsigned long deltaTime) {
    -    stepTimer += scaledDelta;
    -
    -    if (stepTimer >= stepDelay) {
    -        // Check if formation hit edge
    -        bool edgeHit = /* ... */;
    -
    -        if (edgeHit) {
    -            moveDirection *= -1; // Reverse direction
    -            // Drop down
    -            for (auto* alien : aliens) {
    -                alien->move(0, ALIEN_DROP_AMOUNT);
    -            }
    -        } else {
    -            // Move horizontally
    -            float dx = moveDirection * ALIEN_STEP_AMOUNT_X;
    -            for (auto* alien : aliens) {
    -                alien->move(dx, 0);
    -            }
    -        }
    -    }
    -}
    -

    Movement Characteristics: - Step-based (not continuous) - Base step delay: 417ms (72 BPM) - Step amount: 2.5 pixels horizontally - Drop amount: 7 pixels when hitting edge - Tempo scales with music tempo factor

    Enemy Shooting System

    Enemies shoot from bottom-most aliens:

    void SpaceInvadersScene::enemyShoot() {
    -    // Find bottom-most aliens (no alien below them)
    -    std::vector<AlienActor*> bottomAliens;
    -    // ... collect bottom aliens ...
    -
    -    // Difficulty-based chance (increases as aliens die)
    -    float t = 1.0f - (alive / total);
    -    int chance = minChance + (t * (maxChance - minChance));
    -
    -    // Random roll
    -    if (roll >= chance) {
    -        // Fire from random bottom alien
    -    }
    -}
    -

    Shooting Rules: - Only bottom-most aliens can shoot - Maximum 4 enemy bullets active at once - Chance increases as more aliens are destroyed - Difficulty scales from 8% to 30% chance

    Code Structure

    File Organization

    SpaceInvaders/
    -├── SpaceInvadersScene.h/cpp    # Main scene
    -├── PlayerActor.h/cpp           # Player ship
    -├── AlienActor.h/cpp            # Enemy aliens
    -├── ProjectileActor.h/cpp       # Bullets
    -├── BunkerActor.h/cpp           # Defensive bunkers
    -├── GameConstants.h             # Game configuration
    -└── AlienSprites.h              # Sprite definitions
    -

    Game Constants

    Key constants defined in GameConstants.h:

    // Formation
    -constexpr int ALIEN_ROWS = 4;
    -constexpr int ALIEN_COLS = 8;
    -
    -// Movement
    -constexpr unsigned long BASE_STEP_DELAY = 417; // 72 BPM
    -constexpr float ALIEN_STEP_AMOUNT_X = 2.5f;
    -constexpr float ALIEN_DROP_AMOUNT = 7.0f;
    -
    -// Projectiles
    -constexpr int MaxProjectiles = 12;
    -constexpr float PROJECTILE_SPEED = 120.0f;
    -
    -// Bunkers
    -constexpr int BUNKER_COUNT = 4;
    -constexpr int BUNKER_WIDTH = 24;
    -constexpr int BUNKER_HEIGHT = 16;
    -

    Design Patterns Used

    1. Object Pooling

    Projectiles are pooled to avoid allocations:

    // Pre-allocate in resetGame()
    -for (int i = 0; i < MaxProjectiles; ++i) {
    -    ProjectileActor* projectile = new ProjectileActor(0, -PROJECTILE_HEIGHT, ProjectileType::PLAYER_BULLET);
    -    projectile->deactivate();
    -    projectiles.push_back(projectile);
    -}
    -
    -// Reuse when shooting
    -for (auto* proj : projectiles) {
    -    if (!proj->isActive()) {
    -        proj->reset(px, py, ProjectileType::PLAYER_BULLET);
    -        break;
    -    }
    -}
    -

    2. State Machine

    Game states: Playing → Paused (on hit) → Game Over

    if (gameOver) {
    -    // Game over state
    -} else if (isPaused) {
    -    // Paused during player explosion
    -    if (!playerExplosion.isActive()) {
    -        respawnPlayerUnderBunker();
    -        isPaused = false;
    -    }
    -} else {
    -    // Normal gameplay
    -}
    -

    3. Formation Controller

    Aliens are moved as a group by the scene, not individually:

    // Scene controls movement
    -for (auto* alien : aliens) {
    -    if (alien->isActive()) {
    -        alien->move(dx, 0); // Scene tells alien to move
    -    }
    -}
    -

    4. Explosion Pool

    Enemy explosions use a fixed pool:

    static constexpr int MaxEnemyExplosions = 8;
    -EnemyExplosion enemyExplosions[MaxEnemyExplosions];
    -
    -void spawnEnemyExplosion(float x, float y) {
    -    // Find first available slot
    -    for (int i = 0; i < MaxEnemyExplosions; ++i) {
    -        if (!enemyExplosions[i].active) {
    -            enemyExplosions[i].active = true;
    -            enemyExplosions[i].x = x;
    -            enemyExplosions[i].y = y;
    -            enemyExplosions[i].remainingMs = 200;
    -            return;
    -        }
    -    }
    -}
    -

    Audio Integration

    Sound Effects

    Player Shoot:

    AudioEvent event{};
    -event.type = WaveType::PULSE;
    -event.frequency = 880.0f;
    -event.duration = 0.08f;
    -event.volume = 0.4f;
    -event.duty = 0.5f;
    -engine.getAudioEngine().playEvent(event);
    -

    Enemy Hit:

    AudioEvent event{};
    -event.type = WaveType::NOISE;
    -event.frequency = 600.0f;
    -event.duration = 0.12f;
    -event.volume = 0.6f;
    -engine.getAudioEngine().playEvent(event);
    -

    Player Hit:

    AudioEvent event{};
    -event.type = WaveType::NOISE;
    -event.frequency = 400.0f;
    -event.duration = 0.18f;
    -event.volume = 0.7f;
    -engine.getAudioEngine().playEvent(event);
    -

    Background Music

    • Uses MusicPlayer with tempo control
    • Tempo dynamically adjusts based on threat level
    • Different tracks for win/lose conditions

    Sprite System

    Sprite Formats

    • 1bpp sprites: Player, most aliens, explosions
    • MultiSprite: CRAB alien type (multi-layer sprite)
    • Animations: Step-based (advances on movement, not time)

    Sprite Scaling

    All sprites use SPRITE_SCALE = 1.25f for larger appearance:

    renderer.drawSprite(PLAYER_SHIP_SPRITE,
    -    static_cast<int>(x),
    -    static_cast<int>(y),
    -    SPRITE_SCALE,  // scaleX
    -    SPRITE_SCALE,  // scaleY
    -    Color::White);
    -

    Performance Optimizations

    1. Entity Pooling

    • Projectiles are pooled (12 max)
    • Explosions are pooled (8 max)
    • Avoids allocations during gameplay

    2. Scene Arena (Optional)

    • Pre-allocates 8KB buffer for entities
    • All entities allocated from arena
    • Zero heap fragmentation

    3. Efficient Collision Detection

    • Sweep tests only for fast projectiles
    • Simple AABB for static/slow objects
    • Early exits in collision loops

    4. Custom Entity Management

    • Bypasses Scene's entity limit by using vectors
    • Direct control over entity lifecycle
    • More efficient for this specific use case

    Game Flow

    Initialization

    1. Create tilemap background
    2. Spawn player at bottom center
    3. Spawn 32 aliens in formation (4×8 grid)
    4. Spawn 4 bunkers evenly spaced
    5. Pre-allocate projectile pool
    6. Start background music

    Gameplay Loop

    1. Update Input: Read player movement and shooting
    2. Update Aliens: Move formation, check edges, enemy shooting
    3. Update Projectiles: Move projectiles, check boundaries
    4. Handle Collisions: Sweep tests for projectiles vs aliens/bunkers/player
    5. Update Effects: Explosion animations
    6. Update Music: Adjust tempo based on alien position
    7. Draw: Background, entities, explosions, HUD

    Game Over Conditions

    • Win: All aliens destroyed
    • Lose: Player lives reach 0 OR aliens reach player Y position

    Respawn System

    1. Player hit → Start explosion animation
    2. Pause gameplay
    3. Wait for explosion to complete
    4. Respawn player under first intact bunker
    5. Resume gameplay

    Key Learnings

    What This Example Demonstrates

    1. Advanced Collision Detection
    2. Sweep tests for fast-moving objects
    3. Accurate hit detection even at high speeds

    4. Efficient Entity Management

    5. Object pooling for frequently created/destroyed entities
    6. Custom management to work around MAX_ENTITIES limit
    7. Scene Arena for zero-fragmentation allocation

    8. Dynamic Audio

    9. Music tempo synchronization with gameplay
    10. Multiple music tracks for different states
    11. Sound effects for game events

    12. Sprite Animations

    13. Step-based animations (tied to game logic, not time)
    14. MultiSprite for complex sprites
    15. Sprite scaling for visual variety

    16. State Management

    17. Game states (playing, paused, game over)
    18. Smooth transitions between states
    19. Respawn system with visual feedback

    20. Formation Movement

    21. Coordinated group movement
    22. Edge detection and direction reversal
    23. Progressive difficulty (tempo increases)

    24. Visual Effects

    25. Explosion animations
    26. Health-based rendering (bunkers)
    27. HUD with score and lives

    Code Examples

    Shooting System

    // Player shooting
    -if (fireInputReady && player->wantsToShoot()) {
    -    // Check if player bullet already active
    -    bool hasPlayerBullet = false;
    -    for (auto* proj : projectiles) {
    -        if (proj->isActive() && proj->getType() == ProjectileType::PLAYER_BULLET) {
    -            hasPlayerBullet = true;
    -            break;
    -        }
    -    }
    -
    -    if (!hasPlayerBullet) {
    -        // Find inactive projectile and reuse it
    -        for (auto* proj : projectiles) {
    -            if (!proj->isActive()) {
    -                float px = player->x + (PLAYER_WIDTH - PROJECTILE_WIDTH) / 2.0f;
    -                float py = player->y - PROJECTILE_HEIGHT;
    -                proj->reset(px, py, ProjectileType::PLAYER_BULLET);
    -
    -                // Play shoot sound
    -                AudioEvent event{};
    -                event.type = WaveType::PULSE;
    -                event.frequency = 880.0f;
    -                event.duration = 0.08f;
    -                event.volume = 0.4f;
    -                engine.getAudioEngine().playEvent(event);
    -                break;
    -            }
    -        }
    -    }
    -}
    -

    Collision Detection with Sweep Test

    // Check projectile vs alien collision
    -Circle startCircle;
    -startCircle.x = proj->getPreviousX() + radius;
    -startCircle.y = proj->getPreviousY() + PROJECTILE_HEIGHT * 0.5f;
    -startCircle.radius = radius;
    -
    -Circle endCircle;
    -endCircle.x = proj->x + radius;
    -endCircle.y = proj->y + PROJECTILE_HEIGHT * 0.5f;
    -endCircle.radius = radius;
    -
    -float tHit = 0.0f;
    -pixelroot32::core::Rect targetBox = alien->getHitBox();
    -
    -if (sweepCircleVsRect(startCircle, endCircle, targetBox, tHit) ||
    -    proj->getHitBox().intersects(targetBox)) {
    -    // Hit! Deactivate projectile and kill alien
    -    proj->deactivate();
    -    alien->kill();
    -    score += alien->getScoreValue();
    -    spawnEnemyExplosion(alien->x + alien->width * 0.5f, 
    -                        alien->y + alien->height * 0.5f);
    -}
    -

    Running the Example

    Prerequisites

    • PixelRoot32 Game Engine installed
    • ESP32 or Native build configured
    • Display and input configured

    Build and Run

    1. Include the example in your project:

      #include "examples/SpaceInvaders/SpaceInvadersScene.h"
      -

    2. In your main.cpp:

      spaceinvaders::SpaceInvadersScene gameScene;
      -
      -void setup() {
      -    engine.init();
      -    gameScene.init();
      -    engine.setScene(&gameScene);
      -}
      -

    3. Build and upload (ESP32) or run (Native)

    Controls

    • LEFT/RIGHT: Move player ship
    • FIRE: Shoot projectile
    • FIRE (on game over): Restart game

    See Also

    \ No newline at end of file diff --git a/site/getting_started/fundamental_concepts/index.html b/site/getting_started/fundamental_concepts/index.html index 3e2c774..3a24eed 100644 --- a/site/getting_started/fundamental_concepts/index.html +++ b/site/getting_started/fundamental_concepts/index.html @@ -1,4 +1,4 @@ - Fundamental Concepts - PixelRoot32 Documentation

    Fundamental Concepts

    Before you start programming, it's important to understand the basic concepts that form PixelRoot32's architecture. This section explains how the engine works at a conceptual level, without going into code details.

    Engine Architecture

    Engine: The Heart of the Engine

    The Engine is the main class that orchestrates the entire system. Think of it as the conductor that coordinates all subsystems:

    • Renderer: Handles drawing everything on screen
    • InputManager: Reads and processes user input (buttons, keyboard)
    • AudioEngine: Generates and plays sounds and music
    • SceneManager: Manages game scenes (menus, levels, etc.)

    The Engine runs the main game loop: an infinite cycle that updates game logic and draws each frame on screen. It also calculates delta time (time elapsed between frames) so the game runs at the same speed regardless of framerate.

    Scene: Organizing Your Game

    A Scene represents a screen or level in your game. For example: - A scene for the main menu - A scene for each game level - A scene for the game over screen - A scene for the pause menu

    Each scene contains and manages a set of entities (characters, enemies, objects, etc.). The scene is responsible for: - Initializing its entities when loaded - Updating the logic of all its entities each frame - Drawing all its visible entities each frame - Managing collisions between entities that can collide

    The Engine can only have one active scene at a time, but you can easily switch between scenes (for example, go from menu to game, or from game to pause menu).

    Entity: The Fundamental Building Blocks

    An Entity is any object in your game that has: - Position (x, y) in the world - Size (width and height) - Visibility (can be visible or not) - Active state (can be enabled or disabled) - Render layer (in what order it's drawn)

    Entities are the foundation of everything in your game: the player, enemies, projectiles, objects, UI elements—everything is an entity or inherits from Entity.

    Each entity has two main methods: - update(): Called each frame to update the entity's logic (movement, animation, etc.) - draw(): Called each frame to draw the entity on screen

    Actor: Entities That Can Collide

    An Actor is a special entity that can participate in the collision system. In addition to everything an Entity has, an Actor has: - Collision layer: Which group it belongs to (e.g., "player", "enemy", "projectile") - Collision mask: Which other groups it can collide with - Hitbox: The shape used to detect collisions (usually a rectangle)

    For example, a player might be on the "player" layer and have a mask that allows it to collide with "enemies" and "obstacles", but not with "other players".

    When two actors collide, the system calls their onCollision() method so they can react (e.g., player loses health, enemy is destroyed, etc.).

    PhysicsActor: Entities with Physics

    A PhysicsActor is an Actor that also has physical properties: - Velocity (vx, vy): Moves automatically according to its velocity - Gravity: Can fall automatically - Friction: Gradually loses velocity - Restitution: Bounces when it collides (like a ball)

    The PhysicsActor updates automatically each frame, applying physics and moving the entity. It can also detect collisions with world boundaries (the walls of the play area).

    Entity Hierarchy

    The relationship between these classes is hierarchical:

    Entity (base)
    + Fundamental Concepts - PixelRoot32 Documentation      

    Fundamental Concepts

    Before you start programming, it's important to understand the basic concepts that form PixelRoot32's architecture. This section explains how the engine works at a conceptual level, without going into code details.

    Engine Architecture

    Engine: The Heart of the Engine

    The Engine is the main class that orchestrates the entire system. Think of it as the conductor that coordinates all subsystems:

    • Renderer: Handles drawing everything on screen
    • InputManager: Reads and processes user input (buttons, keyboard)
    • AudioEngine: Generates and plays sounds and music
    • SceneManager: Manages game scenes (menus, levels, etc.)

    The Engine runs the main game loop: an infinite cycle that updates game logic and draws each frame on screen. It also calculates delta time (time elapsed between frames) so the game runs at the same speed regardless of framerate.

    Scene: Organizing Your Game

    A Scene represents a screen or level in your game. For example: - A scene for the main menu - A scene for each game level - A scene for the game over screen - A scene for the pause menu

    Each scene contains and manages a set of entities (characters, enemies, objects, etc.). The scene is responsible for: - Initializing its entities when loaded - Updating the logic of all its entities each frame - Drawing all its visible entities each frame - Managing collisions between entities that can collide

    The Engine can only have one active scene at a time, but you can easily switch between scenes (for example, go from menu to game, or from game to pause menu).

    Entity: The Fundamental Building Blocks

    An Entity is any object in your game that has: - Position (x, y) in the world - Size (width and height) - Visibility (can be visible or not) - Active state (can be enabled or disabled) - Render layer (in what order it's drawn)

    Entities are the foundation of everything in your game: the player, enemies, projectiles, objects, UI elements—everything is an entity or inherits from Entity.

    Each entity has two main methods: - update(): Called each frame to update the entity's logic (movement, animation, etc.) - draw(): Called each frame to draw the entity on screen

    Actor: Entities That Can Collide

    An Actor is a special entity that can participate in the collision system. In addition to everything an Entity has, an Actor has: - Collision layer: Which group it belongs to (e.g., "player", "enemy", "projectile") - Collision mask: Which other groups it can collide with - Hitbox: The shape used to detect collisions (usually a rectangle)

    For example, a player might be on the "player" layer and have a mask that allows it to collide with "enemies" and "obstacles", but not with "other players".

    When two actors collide, the system calls their onCollision() method so they can react (e.g., player loses health, enemy is destroyed, etc.).

    PhysicsActor: Entities with Physics

    A PhysicsActor is an Actor that also has physical properties: - Velocity (vx, vy): Moves automatically according to its velocity - Gravity: Can fall automatically - Friction: Gradually loses velocity - Restitution: Bounces when it collides (like a ball)

    The PhysicsActor updates automatically each frame, applying physics and moving the entity. It can also detect collisions with world boundaries (the walls of the play area).

    Entity Hierarchy

    The relationship between these classes is hierarchical:

    Entity (base)
       └── Actor (can collide)
            └── PhysicsActor (has physics)
     

    This means: - Every Actor is also an Entity - Every PhysicsActor is also an Actor and an Entity - You can use Entity for simple objects that don't need collisions - You can use Actor for objects that need to detect collisions - You can use PhysicsActor for objects that need automatic physics

    Rendering System

    Render Layers

    To control the order in which things are drawn, PixelRoot32 uses render layers:

    • Layer 0 (Background): Backgrounds, tilemaps, background elements
    • Layer 1 (Gameplay): Characters, enemies, projectiles, game objects
    • Layer 2 (UI): Menus, HUD, text, interface elements

    Layers are drawn in order: first 0, then 1, and finally 2. This ensures the background is always behind, gameplay in the middle, and UI always visible in front.

    Each entity has a renderLayer property that indicates which layer it should be drawn on. You can change this property to move entities between layers.

    Rendering Pipeline

    The rendering process works like this:

    1. beginFrame(): The screen is cleared (painted black or background color)
    2. Draw entities: All visible entities are traversed, organized by layer
    3. endFrame(): The complete frame is sent to the display

    The Renderer abstracts hardware details, so the same code works on both ESP32 (TFT_eSPI) and PC (SDL2).

    Coordinates and Space

    PixelRoot32 uses a standard coordinate system: - Origin (0, 0): Top-left corner - X-axis: Increases to the right - Y-axis: Increases downward

    Coordinates are in pixels. If your display is 240x240, coordinates range from (0, 0) to (239, 239).

    Lifecycle

    Initialization

    When your game starts:

    1. Configuration: Configuration objects are created (DisplayConfig, InputConfig, AudioConfig)
    2. Engine: The Engine is created with these configurations
    3. init(): engine.init() is called to initialize all subsystems
    4. Scene: The initial scene is created and configured
    5. setScene(): The scene is assigned to the Engine

    Game Loop

    Once initialized, the Engine enters the game loop:

    While the game is running:
    @@ -9,4 +9,4 @@
       5. Detect collisions in the scene
       6. Draw the scene (draw all visible entities)
       7. Repeat
    -

    This cycle runs continuously, typically at 30-60 FPS on ESP32, or faster on PC.

    Update

    Each frame, all enabled entities receive a call to their update(deltaTime) method. This is where: - Entities move - Animations update - Game logic is processed - User input is read - Sound effects are played

    The deltaTime is passed in milliseconds and represents how much time has passed since the last frame. This allows movement to be framerate-independent.

    Rendering (Draw)

    After updating, all visible entities receive a call to their draw(renderer) method. This is where: - Sprites are drawn - Text is drawn - Primitives are drawn (rectangles, circles, etc.)

    The renderer is passed as a parameter so entities can draw themselves.

    Cleanup

    When you change scenes or end the game: - Entities from the previous scene can be cleaned up - Resources are freed - The new scene is initialized

    Conceptual Summary

    To summarize, PixelRoot32 works like this:

    1. Engine coordinates everything and runs the game loop
    2. Scene organizes your game into screens/levels
    3. Entity is any object in your game
    4. Actor is an entity that can collide
    5. PhysicsActor is an actor with automatic physics
    6. Renderer draws everything on screen using layers
    7. Each frame updates logic and then draws

    All of this works automatically once you configure the Engine and create your scenes and entities. You don't need to worry about game loop details; you just need to implement update() and draw() in your entities.

    Next Step

    Now that you understand the fundamental concepts, you're ready to create your first project and see these concepts in action with real code.


    See also: - What is PixelRoot32? - Why PixelRoot32? - Your First Project - Manual - Scenes and Entities

    \ No newline at end of file +

    This cycle runs continuously, typically at 30-60 FPS on ESP32, or faster on PC.

    Update

    Each frame, all enabled entities receive a call to their update(deltaTime) method. This is where: - Entities move - Animations update - Game logic is processed - User input is read - Sound effects are played

    The deltaTime is passed in milliseconds and represents how much time has passed since the last frame. This allows movement to be framerate-independent.

    Rendering (Draw)

    After updating, all visible entities receive a call to their draw(renderer) method. This is where: - Sprites are drawn - Text is drawn - Primitives are drawn (rectangles, circles, etc.)

    The renderer is passed as a parameter so entities can draw themselves.

    Cleanup

    When you change scenes or end the game: - Entities from the previous scene can be cleaned up - Resources are freed - The new scene is initialized

    Conceptual Summary

    To summarize, PixelRoot32 works like this:

    1. Engine coordinates everything and runs the game loop
    2. Scene organizes your game into screens/levels
    3. Entity is any object in your game
    4. Actor is an entity that can collide
    5. PhysicsActor is an actor with automatic physics
    6. Renderer draws everything on screen using layers
    7. Each frame updates logic and then draws

    All of this works automatically once you configure the Engine and create your scenes and entities. You don't need to worry about game loop details; you just need to implement update() and draw() in your entities.

    Next Step

    Now that you understand the fundamental concepts, you're ready to create your first project and see these concepts in action with real code.


    See also: - What is PixelRoot32? - Why PixelRoot32? - Your First Project - Manual - Scenes and Entities

    \ No newline at end of file diff --git a/site/getting_started/installation/index.html b/site/getting_started/installation/index.html index 00b4f4f..fa12c17 100644 --- a/site/getting_started/installation/index.html +++ b/site/getting_started/installation/index.html @@ -1,3 +1,3 @@ - Installation - PixelRoot32 Documentation

    Installation

    This guide covers installing the PixelRoot32 documentation environment and preparing your development setup for ESP32 and Native (PC) targets.

    Requirements

    • Python 3.11 or newer
    • Git (recommended for source management)
    • VS Code (or your preferred IDE)
    • For ESP32 targets: PlatformIO (VS Code extension) with ESP32 toolchain
    • For Native targets: a C++ build toolchain (CMake or your OS-native toolchain)

    Install Documentation Tooling

    To build and preview this documentation locally:

    pip install mkdocs mkdocs-material mkdocs-minify-plugin mkdocs-git-revision-date-localized-plugin mike
    + Installation - PixelRoot32 Documentation      

    Installation

    This guide covers installing the PixelRoot32 documentation environment and preparing your development setup for ESP32 and Native (PC) targets.

    Requirements

    • Python 3.11 or newer
    • Git (recommended for source management)
    • VS Code (or your preferred IDE)
    • For ESP32 targets: PlatformIO (VS Code extension) with ESP32 toolchain
    • For Native targets: a C++ build toolchain (CMake or your OS-native toolchain)

    Install Documentation Tooling

    To build and preview this documentation locally:

    pip install mkdocs mkdocs-material mkdocs-minify-plugin mkdocs-git-revision-date-localized-plugin mike
     mkdocs serve
    -

    Open http://127.0.0.1:8000 in your browser to preview.

    1. Install VS Code
    2. Install PlatformIO IDE extension
    3. Install ESP32 platform/toolchain via PlatformIO
    4. Clone the engine repository:
    5. https://github.com/Gperez88/PixelRoot32-Game-Engine
    6. Open the engine or example project in VS Code (PlatformIO)
    7. Build and upload to your ESP32 board

    Tip: Use boards based on ESP32-WROOM/WROVER for best compatibility. Ensure a reliable USB cable and correct serial port selection.

    Native (PC) Setup

    1. Install a C++ toolchain (e.g., MSVC or MinGW on Windows)
    2. Install CMake (if the engine provides CMake build files)
    3. Clone the engine repository:
    4. https://github.com/Gperez88/PixelRoot32-Game-Engine
    5. Configure and build the native runtime:
    6. Follow the engine’s native build instructions (Development → Compiling)

    Verify Your Environment

    • ESP32: Build and flash a minimal sample; confirm serial output and display if applicable
    • Native: Run the executable; confirm window output and input handling

    Troubleshooting

    • If PlatformIO cannot find the ESP32 platform, update PlatformIO and retry
    • If native builds fail, verify compiler versions and CMake generator settings
    • Use Community → Troubleshooting for common issues and fixes

    Next Steps

    \ No newline at end of file +

    Open http://127.0.0.1:8000 in your browser to preview.

    1. Install VS Code
    2. Install PlatformIO IDE extension
    3. Install ESP32 platform/toolchain via PlatformIO
    4. Clone the engine repository:
    5. https://github.com/Gperez88/PixelRoot32-Game-Engine
    6. Open the engine or example project in VS Code (PlatformIO)
    7. Build and upload to your ESP32 board

    Tip: Use boards based on ESP32-WROOM/WROVER for best compatibility. Ensure a reliable USB cable and correct serial port selection.

    Native (PC) Setup

    1. Install a C++ toolchain (e.g., MSVC or MinGW on Windows)
    2. Install CMake (if the engine provides CMake build files)
    3. Clone the engine repository:
    4. https://github.com/Gperez88/PixelRoot32-Game-Engine
    5. Configure and build the native runtime:
    6. Follow the engine’s native build instructions (Development → Compiling)

    Verify Your Environment

    • ESP32: Build and flash a minimal sample; confirm serial output and display if applicable
    • Native: Run the executable; confirm window output and input handling

    Troubleshooting

    • If PlatformIO cannot find the ESP32 platform, update PlatformIO and retry
    • If native builds fail, verify compiler versions and CMake generator settings
    • Use Community → Troubleshooting for common issues and fixes

    Next Steps

    \ No newline at end of file diff --git a/site/getting_started/what_is_pixelroot32/index.html b/site/getting_started/what_is_pixelroot32/index.html index 8e2b462..caabcb1 100644 --- a/site/getting_started/what_is_pixelroot32/index.html +++ b/site/getting_started/what_is_pixelroot32/index.html @@ -1 +1 @@ - What is PixelRoot32? - PixelRoot32 Documentation

    What is PixelRoot32?

    PixelRoot32 is a lightweight, modular 2D game engine written in C++ designed specifically for ESP32 microcontrollers, with a native simulation layer for PC (SDL2) that allows you to develop and debug quickly on your desktop before deploying to hardware.

    Simple Definition

    PixelRoot32 is a game engine that lets you create retro-style 8-bit/16-bit video games directly on an ESP32 board, with the ability to develop and test on your PC before transferring code to hardware.

    Key Features

    🎮 Scene-Based Architecture

    • Scene system inspired by Godot Engine
    • Intuitive management of levels, menus, and screens
    • Simple transitions between scenes

    🎨 Optimized Rendering

    • 1bpp (monochrome) sprites as the standard format
    • Support for multi-layer sprites (MultiSprite)
    • Experimental 2bpp and 4bpp formats for higher fidelity
    • Retro color palette system (NES, GameBoy, PICO-8, etc.)
    • Compact tilemaps for backgrounds and levels
    • 2D camera with dead-zone for smooth scrolling
    • Render layer system (background, gameplay, UI)

    🔊 NES-like Audio

    • 4 audio channels (2 Pulse, 1 Triangle, 1 Noise)
    • Integrated sound effects system
    • Music player for background melodies
    • Backends for ESP32 (internal DAC or external I2S) and SDL2

    🎯 Physics and Collisions

    • AABB (Axis-Aligned Bounding Box) collision system
    • PhysicsActor with gravity, friction, and restitution
    • Collision layers and masks for fine control
    • World boundary collision detection

    🖥️ User Interface

    • Basic elements: Labels, Buttons, Panels
    • Automatic layouts: Vertical, Horizontal, Grid, Anchor
    • Integrated D-pad navigation
    • Scroll and viewport culling for long lists

    ⚡ Optimized for ESP32

    • Efficient memory management
    • Integrated object pooling
    • No dynamic allocations in the game loop
    • Performance optimized for limited hardware

    Typical Use Cases

    PixelRoot32 is ideal for creating:

    • Arcade Games: Space Invaders, Pong, Breakout
    • Platformers: Horizontal scrolling games with simple physics
    • Puzzles: Tetris, Snake, logic games
    • Simple RPGs: Basic role-playing games with tilemaps
    • Shooters: Vertical or horizontal shooting games
    • Rapid Prototypes: Quick development of game ideas

    Supported Platforms

    ESP32

    • Display: TFT_eSPI (ST7735, ILI9341, ST7789, etc.)
    • Audio: Internal DAC (GPIO 25/26) or external I2S (MAX98357A, PCM5102)
    • Input: Digital buttons, D-pad
    • Hardware: Any ESP32 board (ESP32-WROOM, ESP32-WROVER, etc.)

    Desktop/Native (PC)

    • Display: SDL2 (Windows, Linux, macOS)
    • Audio: SDL2 Audio
    • Input: Keyboard, mouse
    • Usage: Development, debugging, testing

    Note: Support for u8g2 (OLEDs) is planned for the future.

    Project Status

    Current Version: v0.2.0-dev

    PixelRoot32 is under active development. APIs may change and some subsystems are still experimental. Occasional changes or breaking changes are expected, especially on less-tested configurations.

    Stable Features

    • Scene and entity system
    • Basic rendering (1bpp sprites)
    • NES-like audio system
    • Basic physics and collisions
    • Basic UI system
    • ESP32 and Native support

    Experimental Features

    • 2bpp and 4bpp sprites (require compilation flags)
    • Scene Arena (advanced memory management)

    Planned Features

    • Support for u8g2 (OLEDs)
    • Music compiler
    • Tilemap compiler
    • Save/load system
    • Spatial partitioning for collisions

    Quick Comparison

    When to use PixelRoot32?

    ✅ Use PixelRoot32 if: - You want to create retro games on ESP32 - You need a lightweight and efficient engine - You prefer a simple and clear architecture - You want to develop on PC and deploy to ESP32 - You like 8-bit/16-bit style games

    ❌ Don't use PixelRoot32 if: - You need 3D graphics - You require advanced shaders - You need complex physics (advanced physics engines) - You want to create modern AAA games - You need support for multiple mobile platforms

    Next Step

    Now that you understand what PixelRoot32 is, discover why you should use it or go directly to your first project.


    See also: - Fundamental Concepts - Installation - API Reference

    \ No newline at end of file + What is PixelRoot32? - PixelRoot32 Documentation

    What is PixelRoot32?

    PixelRoot32 is a lightweight, modular 2D game engine written in C++ designed specifically for ESP32 microcontrollers, with a native simulation layer for PC (SDL2) that allows you to develop and debug quickly on your desktop before deploying to hardware.

    Simple Definition

    PixelRoot32 is a game engine that lets you create retro-style 8-bit/16-bit video games directly on an ESP32 board, with the ability to develop and test on your PC before transferring code to hardware.

    Key Features

    🎮 Scene-Based Architecture

    • Scene system inspired by Godot Engine
    • Intuitive management of levels, menus, and screens
    • Simple transitions between scenes

    🎨 Optimized Rendering

    • 1bpp (monochrome) sprites as the standard format
    • Support for multi-layer sprites (MultiSprite)
    • Experimental 2bpp and 4bpp formats for higher fidelity
    • Retro color palette system (NES, GameBoy, PICO-8, etc.)
    • Compact tilemaps for backgrounds and levels
    • 2D camera with dead-zone for smooth scrolling
    • Render layer system (background, gameplay, UI)

    🔊 NES-like Audio

    • 4 audio channels (2 Pulse, 1 Triangle, 1 Noise)
    • Integrated sound effects system
    • Music player for background melodies
    • Backends for ESP32 (internal DAC or external I2S) and SDL2

    🎯 Physics and Collisions

    • AABB (Axis-Aligned Bounding Box) collision system
    • PhysicsActor with gravity, friction, and restitution
    • Collision layers and masks for fine control
    • World boundary collision detection

    🖥️ User Interface

    • Basic elements: Labels, Buttons, Panels
    • Automatic layouts: Vertical, Horizontal, Grid, Anchor
    • Integrated D-pad navigation
    • Scroll and viewport culling for long lists

    ⚡ Optimized for ESP32

    • Efficient memory management
    • Integrated object pooling
    • No dynamic allocations in the game loop
    • Performance optimized for limited hardware

    Typical Use Cases

    PixelRoot32 is ideal for creating:

    • Arcade Games: Space Invaders, Pong, Breakout
    • Platformers: Horizontal scrolling games with simple physics
    • Puzzles: Tetris, Snake, logic games
    • Simple RPGs: Basic role-playing games with tilemaps
    • Shooters: Vertical or horizontal shooting games
    • Rapid Prototypes: Quick development of game ideas

    Supported Platforms

    ESP32

    • Display: TFT_eSPI (ST7735, ILI9341, ST7789, etc.)
    • Audio: Internal DAC (GPIO 25/26) or external I2S (MAX98357A, PCM5102)
    • Input: Digital buttons, D-pad
    • Hardware: Any ESP32 board (ESP32-WROOM, ESP32-WROVER, etc.)

    Desktop/Native (PC)

    • Display: SDL2 (Windows, Linux, macOS)
    • Audio: SDL2 Audio
    • Input: Keyboard, mouse
    • Usage: Development, debugging, testing

    Note: Support for u8g2 (OLEDs) is planned for the future.

    Project Status

    Current Version: v0.2.0-dev

    PixelRoot32 is under active development. APIs may change and some subsystems are still experimental. Occasional changes or breaking changes are expected, especially on less-tested configurations.

    Stable Features

    • Scene and entity system
    • Basic rendering (1bpp sprites)
    • NES-like audio system
    • Basic physics and collisions
    • Basic UI system
    • ESP32 and Native support

    Experimental Features

    • 2bpp and 4bpp sprites (require compilation flags)
    • Scene Arena (advanced memory management)

    Planned Features

    • Support for u8g2 (OLEDs)
    • Music compiler
    • Tilemap compiler
    • Save/load system
    • Spatial partitioning for collisions

    Quick Comparison

    When to use PixelRoot32?

    ✅ Use PixelRoot32 if: - You want to create retro games on ESP32 - You need a lightweight and efficient engine - You prefer a simple and clear architecture - You want to develop on PC and deploy to ESP32 - You like 8-bit/16-bit style games

    ❌ Don't use PixelRoot32 if: - You need 3D graphics - You require advanced shaders - You need complex physics (advanced physics engines) - You want to create modern AAA games - You need support for multiple mobile platforms

    Next Step

    Now that you understand what PixelRoot32 is, discover why you should use it or go directly to your first project.


    See also: - Fundamental Concepts - Installation - API Reference

    \ No newline at end of file diff --git a/site/getting_started/why_pixelroot32/index.html b/site/getting_started/why_pixelroot32/index.html index 71f8dc7..572906f 100644 --- a/site/getting_started/why_pixelroot32/index.html +++ b/site/getting_started/why_pixelroot32/index.html @@ -1 +1 @@ - Why PixelRoot32? - PixelRoot32 Documentation

    Why PixelRoot32?

    PixelRoot32 is specifically designed to solve the unique challenges of creating video games on embedded hardware like the ESP32, while maintaining the simplicity and productivity of modern development.

    Main Advantages

    🎯 Optimized for ESP32

    Memory Efficient - 1bpp sprite system that minimizes RAM and Flash usage - Integrated object pooling to avoid memory fragmentation - Compact tilemaps that reuse sprites - No dynamic allocations in the game loop

    Performance Optimized - Rendering optimized for ESP32 limitations - Efficient render layer system - Viewport culling to reduce draw calls - Rendering pipeline designed for limited hardware

    Real Hardware - Direct support for common TFT displays (ST7735, ILI9341, ST7789) - Integrated audio (internal DAC or external I2S) - Simple pin and hardware configuration

    🖥️ Cross-Platform Development

    Develop on PC, Deploy to ESP32 - Same code works on PC (SDL2) and ESP32 - Fast debugging on desktop - Testing without hardware needed - Rapid development iteration

    Visual Consistency - Native bitmap font system (pixel-perfect) - Same rendering on PC and ESP32 - Consistent color palettes - No surprises when transferring to hardware

    🎨 Retro Palette System

    Authentic Style - Predefined palettes: NES, GameBoy, GameBoy Color, PICO-8 - Dual palette mode for visual contrasts - Custom palettes for unique styles - Automatic color resolution (RGB565)

    Easy to Use - Change palette with one line of code - Consistent visualization across all sprites - No need to manually convert assets

    🔊 Integrated Audio

    Complete NES-like System - 4 audio channels (2 Pulse, 1 Triangle, 1 Noise) - Simple sound effects to create - Integrated music system - Backends for different hardware configurations

    No External Dependencies - Software-generated audio - No heavy audio libraries required - Full control over sound - Deterministic and predictable

    🏗️ Simple and Clear Architecture

    Easy to Understand - Intuitive scene system (inspired by Godot) - Clear hierarchy: Entity → Actor → PhysicsActor - Consistent and predictable APIs - Clean and well-organized code

    Quick to Learn - Familiar concepts for game developers - Clear documentation and complete examples - Smooth learning curve - Active community and support

    🎮 Complete Features

    Everything Needed for Games - Rendering (sprites, tilemaps, primitives) - Audio (effects and music) - Physics (gravity, collisions, basic physics) - UI (layouts, buttons, navigation) - Input (buttons, keyboard) - Camera (scroll, parallax)

    No Bloat - Only the essentials, nothing more - No heavy dependencies - Small and maintainable codebase - Easy to understand and modify

    🛠️ Tools and Ecosystem

    Available Tools - Sprite Compiler to convert PNG to sprites - Complete game examples - Templates and starter code - Extensive documentation

    Community and Support - Active and developing project - Open source (MIT License) - Feedback and contributions welcome - Examples available

    Comparison with Alternatives

    vs. Full Engines (Unity, Godot, etc.)

    PixelRoot32 Advantages: - ✅ Much lighter (fits in ESP32) - ✅ No unnecessary overhead - ✅ Full control over code - ✅ Specifically optimized for limited hardware

    Disadvantages: - ❌ Fewer advanced features - ❌ No visual editor - ❌ Fewer resources and community

    vs. Writing Everything from Scratch

    PixelRoot32 Advantages: - ✅ Rendering system already implemented - ✅ Integrated and working audio - ✅ Physics and collisions ready - ✅ Complete UI system - ✅ Saves months of development

    Disadvantages: - ❌ Less control over internal implementation - ❌ You must learn the engine API

    vs. Other ESP32 Engines

    PixelRoot32 Advantages: - ✅ More modern and clear architecture - ✅ Better documentation - ✅ Unique palette system - ✅ Integrated NES-like audio - ✅ Real cross-platform development

    Ideal Use Cases

    PixelRoot32 is perfect for:

    1. Educational Projects
    2. Learn game development
    3. Understand engine architecture
    4. Student projects

    5. Rapid Prototypes

    6. Quickly validate game ideas
    7. Create demos and proof-of-concepts
    8. Test mechanics

    9. Retro Games

    10. 8-bit/16-bit style games
    11. Arcade games
    12. Games with retro aesthetics

    13. Hardware Projects

    14. Games on small displays
    15. DIY portable consoles
    16. Maker/retro projects

    17. C++ Learning

    18. Clean and well-structured code
    19. Good programming practices
    20. Real and functional examples

    Limitations to Consider

    To be honest, PixelRoot32 has limitations:

    • Limited Hardware: Designed for ESP32, not powerful PCs
    • Simple Graphics: No 3D, no advanced shaders
    • Basic Physics: Not a complete physics engine
    • Restricted Memory: MAX_ENTITIES = 32 per scene
    • In Development: Some features are experimental

    If you need advanced features or powerful hardware, consider other engines. But for retro games on ESP32, PixelRoot32 is an excellent choice.

    Conclusion

    PixelRoot32 combines:

    • Simplicity of use
    • Efficiency for limited hardware
    • Completeness of essential features
    • Clarity of architecture
    • Productivity in development

    If you want to create retro games on ESP32 without the complexity of large engines, PixelRoot32 is the right choice.

    Next Step

    Now that you understand why PixelRoot32 is a good option, learn the fundamental concepts or start directly with your first project.


    See also: - What is PixelRoot32? - Fundamental Concepts - Your First Project

    \ No newline at end of file + Why PixelRoot32? - PixelRoot32 Documentation

    Why PixelRoot32?

    PixelRoot32 is specifically designed to solve the unique challenges of creating video games on embedded hardware like the ESP32, while maintaining the simplicity and productivity of modern development.

    Main Advantages

    🎯 Optimized for ESP32

    Memory Efficient - 1bpp sprite system that minimizes RAM and Flash usage - Integrated object pooling to avoid memory fragmentation - Compact tilemaps that reuse sprites - No dynamic allocations in the game loop

    Performance Optimized - Rendering optimized for ESP32 limitations - Efficient render layer system - Viewport culling to reduce draw calls - Rendering pipeline designed for limited hardware

    Real Hardware - Direct support for common TFT displays (ST7735, ILI9341, ST7789) - Integrated audio (internal DAC or external I2S) - Simple pin and hardware configuration

    🖥️ Cross-Platform Development

    Develop on PC, Deploy to ESP32 - Same code works on PC (SDL2) and ESP32 - Fast debugging on desktop - Testing without hardware needed - Rapid development iteration

    Visual Consistency - Native bitmap font system (pixel-perfect) - Same rendering on PC and ESP32 - Consistent color palettes - No surprises when transferring to hardware

    🎨 Retro Palette System

    Authentic Style - Predefined palettes: NES, GameBoy, GameBoy Color, PICO-8 - Dual palette mode for visual contrasts - Custom palettes for unique styles - Automatic color resolution (RGB565)

    Easy to Use - Change palette with one line of code - Consistent visualization across all sprites - No need to manually convert assets

    🔊 Integrated Audio

    Complete NES-like System - 4 audio channels (2 Pulse, 1 Triangle, 1 Noise) - Simple sound effects to create - Integrated music system - Backends for different hardware configurations

    No External Dependencies - Software-generated audio - No heavy audio libraries required - Full control over sound - Deterministic and predictable

    🏗️ Simple and Clear Architecture

    Easy to Understand - Intuitive scene system (inspired by Godot) - Clear hierarchy: Entity → Actor → PhysicsActor - Consistent and predictable APIs - Clean and well-organized code

    Quick to Learn - Familiar concepts for game developers - Clear documentation and complete examples - Smooth learning curve - Active community and support

    🎮 Complete Features

    Everything Needed for Games - Rendering (sprites, tilemaps, primitives) - Audio (effects and music) - Physics (gravity, collisions, basic physics) - UI (layouts, buttons, navigation) - Input (buttons, keyboard) - Camera (scroll, parallax)

    No Bloat - Only the essentials, nothing more - No heavy dependencies - Small and maintainable codebase - Easy to understand and modify

    🛠️ Tools and Ecosystem

    Available Tools - Sprite Compiler to convert PNG to sprites - Complete game examples - Templates and starter code - Extensive documentation

    Community and Support - Active and developing project - Open source (MIT License) - Feedback and contributions welcome - Examples available

    Comparison with Alternatives

    vs. Full Engines (Unity, Godot, etc.)

    PixelRoot32 Advantages: - ✅ Much lighter (fits in ESP32) - ✅ No unnecessary overhead - ✅ Full control over code - ✅ Specifically optimized for limited hardware

    Disadvantages: - ❌ Fewer advanced features - ❌ No visual editor - ❌ Fewer resources and community

    vs. Writing Everything from Scratch

    PixelRoot32 Advantages: - ✅ Rendering system already implemented - ✅ Integrated and working audio - ✅ Physics and collisions ready - ✅ Complete UI system - ✅ Saves months of development

    Disadvantages: - ❌ Less control over internal implementation - ❌ You must learn the engine API

    vs. Other ESP32 Engines

    PixelRoot32 Advantages: - ✅ More modern and clear architecture - ✅ Better documentation - ✅ Unique palette system - ✅ Integrated NES-like audio - ✅ Real cross-platform development

    Ideal Use Cases

    PixelRoot32 is perfect for:

    1. Educational Projects
    2. Learn game development
    3. Understand engine architecture
    4. Student projects

    5. Rapid Prototypes

    6. Quickly validate game ideas
    7. Create demos and proof-of-concepts
    8. Test mechanics

    9. Retro Games

    10. 8-bit/16-bit style games
    11. Arcade games
    12. Games with retro aesthetics

    13. Hardware Projects

    14. Games on small displays
    15. DIY portable consoles
    16. Maker/retro projects

    17. C++ Learning

    18. Clean and well-structured code
    19. Good programming practices
    20. Real and functional examples

    Limitations to Consider

    To be honest, PixelRoot32 has limitations:

    • Limited Hardware: Designed for ESP32, not powerful PCs
    • Simple Graphics: No 3D, no advanced shaders
    • Basic Physics: Not a complete physics engine
    • Restricted Memory: MAX_ENTITIES = 32 per scene
    • In Development: Some features are experimental

    If you need advanced features or powerful hardware, consider other engines. But for retro games on ESP32, PixelRoot32 is an excellent choice.

    Conclusion

    PixelRoot32 combines:

    • Simplicity of use
    • Efficiency for limited hardware
    • Completeness of essential features
    • Clarity of architecture
    • Productivity in development

    If you want to create retro games on ESP32 without the complexity of large engines, PixelRoot32 is the right choice.

    Next Step

    Now that you understand why PixelRoot32 is a good option, learn the fundamental concepts or start directly with your first project.


    See also: - What is PixelRoot32? - Fundamental Concepts - Your First Project

    \ No newline at end of file diff --git a/site/getting_started/your_first_project/index.html b/site/getting_started/your_first_project/index.html index f7e50ae..2cb3b9e 100644 --- a/site/getting_started/your_first_project/index.html +++ b/site/getting_started/your_first_project/index.html @@ -1,4 +1,4 @@ - Your First Project - PixelRoot32 Documentation

    Your First Project

    This guide will walk you through creating and running your first PixelRoot32 project step by step. By the end, you'll have a working project that displays a simple scene on both ESP32 and PC.

    Prerequisites

    Required Software

    • PlatformIO: Install the PlatformIO IDE extension in VS Code
    • Open VS Code
    • Go to Extensions (Ctrl+Shift+X)
    • Search for "PlatformIO IDE"
    • Install and restart VS Code

    • Python 3.8+: Required for PlatformIO (usually installed automatically)

    For ESP32 Development

    • ESP32 Board: Any ESP32 development board (ESP32-WROOM, ESP32-WROVER, etc.)
    • USB Cable: To connect and program your ESP32
    • TFT Display: Compatible display (ST7735, ST7789, ILI9341, etc.)
    • Buttons: 5-6 digital buttons for input (optional for first project)
    • Audio Hardware (optional): Speaker + amplifier (PAM8302A) or I2S DAC (MAX98357A)

    For Native (PC) Development

    • SDL2: Development libraries
    • Windows (MSYS2): pacman -S mingw-w64-x86_64-SDL2
    • Linux: sudo apt-get install libsdl2-dev
    • macOS: brew install sdl2

    Step 1: Create a New PlatformIO Project

    1. Open VS Code with PlatformIO installed

    2. Create New Project:

    3. Click on the PlatformIO icon in the sidebar
    4. Click "New Project"
    5. Name: my-first-pixelroot32-game
    6. Board: Select "ESP32 Dev Module" (or your specific board)
    7. Framework: Arduino
    8. Location: Choose your workspace folder
    9. Click "Finish"

    10. Project Structure: Your project should now have this structure:

      my-first-pixelroot32-game/
      + Your First Project - PixelRoot32 Documentation      

      Your First Project

      This guide will walk you through creating and running your first PixelRoot32 project step by step. By the end, you'll have a working project that displays a simple scene on both ESP32 and PC.

      Prerequisites

      Required Software

      • PlatformIO: Install the PlatformIO IDE extension in VS Code
      • Open VS Code
      • Go to Extensions (Ctrl+Shift+X)
      • Search for "PlatformIO IDE"
      • Install and restart VS Code

      • Python 3.8+: Required for PlatformIO (usually installed automatically)

      For ESP32 Development

      • ESP32 Board: Any ESP32 development board (ESP32-WROOM, ESP32-WROVER, etc.)
      • USB Cable: To connect and program your ESP32
      • TFT Display: Compatible display (ST7735, ST7789, ILI9341, etc.)
      • Buttons: 5-6 digital buttons for input (optional for first project)
      • Audio Hardware (optional): Speaker + amplifier (PAM8302A) or I2S DAC (MAX98357A)

      For Native (PC) Development

      • SDL2: Development libraries
      • Windows (MSYS2): pacman -S mingw-w64-x86_64-SDL2
      • Linux: sudo apt-get install libsdl2-dev
      • macOS: brew install sdl2

      Step 1: Create a New PlatformIO Project

      1. Open VS Code with PlatformIO installed

      2. Create New Project:

      3. Click on the PlatformIO icon in the sidebar
      4. Click "New Project"
      5. Name: my-first-pixelroot32-game
      6. Board: Select "ESP32 Dev Module" (or your specific board)
      7. Framework: Arduino
      8. Location: Choose your workspace folder
      9. Click "Finish"

      10. Project Structure: Your project should now have this structure:

        my-first-pixelroot32-game/
         ├── .pio/
         ├── include/
         ├── lib/
        @@ -226,4 +226,4 @@
             -std=c++17
             -lSDL2
             -mconsole
        -

        Note: Adjust the SDL2 include and library paths for your system.

        Step 7: Build and Run

        For ESP32

        1. Connect your ESP32 via USB
        2. Select the environment: Click on the PlatformIO icon → Select env:esp32dev
        3. Build: Click the checkmark icon (✓) or press Ctrl+Alt+B
        4. Upload: Click the arrow icon (→) or press Ctrl+Alt+U
        5. Monitor: Click the plug icon to open serial monitor

        You should see "PixelRoot32 initialized!" in the serial monitor and your display should show a blue rectangle and text.

        For Native (PC)

        1. Select the environment: Click on the PlatformIO icon → Select env:native
        2. Build and Run: Click the play icon (▶) or press Ctrl+Alt+R

        A window should open showing your scene with a blue rectangle and "Hello PixelRoot32!" text.

        Step 8: Verify It Works

        If everything is set up correctly, you should see:

        • ESP32: Display shows a blue rectangle at (50, 50) and white text "Hello PixelRoot32!" at (20, 20)
        • Native: Window shows the same content

        If you see this, congratulations! Your first PixelRoot32 project is working.

        Troubleshooting

        ESP32 Issues

        Display is blank: - Check wiring connections - Verify pin numbers in platformio.ini match your hardware - Check SPI frequency (try lowering it) - Verify display type (ST7789 vs ST7735)

        Compilation errors: - Ensure library version is exactly 0.2.0-dev - Check that TFT_eSPI is installed - Verify all include paths are correct

        Upload fails: - Check USB cable connection - Try different USB port - Press BOOT button on ESP32 during upload - Check COM port in PlatformIO

        Native Issues

        SDL2 not found: - Verify SDL2 is installed - Check include/library paths in platformio.ini - On Windows, ensure MSYS2 paths are correct

        Window doesn't open: - Check console for error messages - Verify SDL2 is properly linked - Try running from terminal to see errors

        Next Steps

        Now that you have a working project, you can:

        1. Learn about Scenes and Entities: See how to create game objects
        2. Add Input: Make your scene respond to buttons
        3. Add Sprites: Draw custom graphics
        4. Add Audio: Play sounds and music

        Continue with the Development Guide to learn more.


        See also: - Fundamental Concepts - Installation - Manual - Scenes and Entities - API Reference

      \ No newline at end of file +

      Note: Adjust the SDL2 include and library paths for your system.

      Step 7: Build and Run

      For ESP32

      1. Connect your ESP32 via USB
      2. Select the environment: Click on the PlatformIO icon → Select env:esp32dev
      3. Build: Click the checkmark icon (✓) or press Ctrl+Alt+B
      4. Upload: Click the arrow icon (→) or press Ctrl+Alt+U
      5. Monitor: Click the plug icon to open serial monitor

      You should see "PixelRoot32 initialized!" in the serial monitor and your display should show a blue rectangle and text.

      For Native (PC)

      1. Select the environment: Click on the PlatformIO icon → Select env:native
      2. Build and Run: Click the play icon (▶) or press Ctrl+Alt+R

      A window should open showing your scene with a blue rectangle and "Hello PixelRoot32!" text.

      Step 8: Verify It Works

      If everything is set up correctly, you should see:

      • ESP32: Display shows a blue rectangle at (50, 50) and white text "Hello PixelRoot32!" at (20, 20)
      • Native: Window shows the same content

      If you see this, congratulations! Your first PixelRoot32 project is working.

      Troubleshooting

      ESP32 Issues

      Display is blank: - Check wiring connections - Verify pin numbers in platformio.ini match your hardware - Check SPI frequency (try lowering it) - Verify display type (ST7789 vs ST7735)

      Compilation errors: - Ensure library version is exactly 0.2.0-dev - Check that TFT_eSPI is installed - Verify all include paths are correct

      Upload fails: - Check USB cable connection - Try different USB port - Press BOOT button on ESP32 during upload - Check COM port in PlatformIO

      Native Issues

      SDL2 not found: - Verify SDL2 is installed - Check include/library paths in platformio.ini - On Windows, ensure MSYS2 paths are correct

      Window doesn't open: - Check console for error messages - Verify SDL2 is properly linked - Try running from terminal to see errors

      Next Steps

      Now that you have a working project, you can:

      1. Learn about Scenes and Entities: See how to create game objects
      2. Add Input: Make your scene respond to buttons
      3. Add Sprites: Draw custom graphics
      4. Add Audio: Play sounds and music

      Continue with the Development Guide to learn more.


      See also: - Fundamental Concepts - Installation - Manual - Scenes and Entities - API Reference

    \ No newline at end of file diff --git a/site/index.html b/site/index.html index 584ea95..ac4ec92 100644 --- a/site/index.html +++ b/site/index.html @@ -1 +1 @@ - PixelRoot32 Documentation

    PixelRoot32 Documentation

    PixelRoot32 is a lightweight 2D game engine designed for ESP32 and native desktop targets. This site provides official, versioned documentation with clear guides, conceptual explanations, API references, and complete examples to help you build games efficiently.

    Getting Started

    New to PixelRoot32? Follow this learning path:

    1. What is PixelRoot32? - Understand what the engine is and what it can do
    2. Why PixelRoot32? - Learn the advantages and use cases
    3. Fundamental Concepts - Learn the core architecture concepts
    4. Your First Project - Create and run your first project

    About This Documentation

    • Professional technical English across all pages
    • Search-enabled, mobile-friendly UI
    • Versioned with mike (stable/dev/experimental)
    • Cross-linked concepts, API, and examples
    • Progressive learning path from basics to advanced topics
    \ No newline at end of file + PixelRoot32 Documentation

    PixelRoot32 Documentation

    PixelRoot32 is a lightweight 2D game engine designed for ESP32 and native desktop targets. This site provides official, versioned documentation with clear guides, conceptual explanations, API references, and complete examples to help you build games efficiently.

    Getting Started

    New to PixelRoot32? Follow this learning path:

    1. What is PixelRoot32? - Understand what the engine is and what it can do
    2. Why PixelRoot32? - Learn the advantages and use cases
    3. Fundamental Concepts - Learn the core architecture concepts
    4. Your First Project - Create and run your first project

    About This Documentation

    • Professional technical English across all pages
    • Search-enabled, mobile-friendly UI
    • Versioned with mike (stable/dev/experimental)
    • Cross-linked concepts, API, and examples
    • Progressive learning path from basics to advanced topics
    \ No newline at end of file diff --git a/site/manual/advanced_graphics/cameras_and_scrolling/index.html b/site/manual/advanced_graphics/cameras_and_scrolling/index.html index 06c4c17..2a9dba9 100644 --- a/site/manual/advanced_graphics/cameras_and_scrolling/index.html +++ b/site/manual/advanced_graphics/cameras_and_scrolling/index.html @@ -1,4 +1,4 @@ - Cameras and Scrolling - PixelRoot32 Documentation

    Cameras and Scrolling

    Camera2D allows you to create worlds larger than the screen by scrolling the view. This guide covers camera setup, following targets, boundaries, and parallax effects.

    Camera2D Basics

    A Camera2D defines what portion of your game world is visible on screen.

    Creating a Camera

    #include <graphics/Camera2D.h>
    + Cameras and Scrolling - PixelRoot32 Documentation      

    Cameras and Scrolling

    Camera2D allows you to create worlds larger than the screen by scrolling the view. This guide covers camera setup, following targets, boundaries, and parallax effects.

    Camera2D Basics

    A Camera2D defines what portion of your game world is visible on screen.

    Creating a Camera

    #include <graphics/Camera2D.h>
     
     // Create camera with viewport size
     pixelroot32::graphics::Camera2D camera(240, 240); // Screen width, height
    @@ -316,4 +316,4 @@
                  y + height < cameraY || 
                  y > cameraY + screenHeight);
     }
    -

    Troubleshooting

    Camera Not Moving

    • Verify camera.apply() is called in draw()
    • Check followTarget() or setPosition() is called in update()
    • Ensure camera is created with correct viewport size
    • Check boundaries aren't preventing movement

    Objects Not Visible

    • Verify objects are within camera view
    • Check world coordinates vs screen coordinates
    • Ensure camera is applied before drawing
    • Verify render layers are correct

    Parallax Not Working

    • Check setDisplayOffset() is used correctly
    • Verify parallax speed values (0.0 to 1.0)
    • Ensure offset is reset after parallax layers
    • Test with different speed values

    Next Steps

    Now that you understand cameras and scrolling, learn about: - Tilemaps - Build levels with tiles - Particles and Effects - Add visual effects - Performance Optimization - Optimize your game


    See also: - API Reference - Camera2D - Manual - Basic Rendering - Manual - Tilemaps

    \ No newline at end of file +

    Troubleshooting

    Camera Not Moving

    • Verify camera.apply() is called in draw()
    • Check followTarget() or setPosition() is called in update()
    • Ensure camera is created with correct viewport size
    • Check boundaries aren't preventing movement

    Objects Not Visible

    • Verify objects are within camera view
    • Check world coordinates vs screen coordinates
    • Ensure camera is applied before drawing
    • Verify render layers are correct

    Parallax Not Working

    • Check setDisplayOffset() is used correctly
    • Verify parallax speed values (0.0 to 1.0)
    • Ensure offset is reset after parallax layers
    • Test with different speed values

    Next Steps

    Now that you understand cameras and scrolling, learn about: - Tilemaps - Build levels with tiles - Particles and Effects - Add visual effects - Performance Optimization - Optimize your game


    See also: - API Reference - Camera2D - Manual - Basic Rendering - Manual - Tilemaps

    \ No newline at end of file diff --git a/site/manual/advanced_graphics/color_palettes/index.html b/site/manual/advanced_graphics/color_palettes/index.html index e7176c0..640a043 100644 --- a/site/manual/advanced_graphics/color_palettes/index.html +++ b/site/manual/advanced_graphics/color_palettes/index.html @@ -1,4 +1,4 @@ - Color Palettes - PixelRoot32 Documentation

    Color Palettes

    PixelRoot32 uses a palette-based color system that allows you to easily change the visual style of your game. This guide covers built-in palettes, dual palette mode, and custom palettes.

    Built-in Palettes

    PixelRoot32 includes several predefined palettes inspired by classic gaming systems:

    Available Palettes

    #include <graphics/PaletteDefs.h>
    + Color Palettes - PixelRoot32 Documentation      

    Color Palettes

    PixelRoot32 uses a palette-based color system that allows you to easily change the visual style of your game. This guide covers built-in palettes, dual palette mode, and custom palettes.

    Built-in Palettes

    PixelRoot32 includes several predefined palettes inspired by classic gaming systems:

    Available Palettes

    #include <graphics/PaletteDefs.h>
     
     namespace pixelroot32::graphics {
     
    @@ -204,4 +204,4 @@
             Color::White
         };
     }
    -

    Troubleshooting

    Colors Not Changing

    • Verify setPalette() is called before drawing
    • Check palette is set in init(), not update()
    • Ensure dual palette mode is enabled if using separate palettes
    • Verify Color constants are from correct namespace

    Colors Look Wrong on Hardware

    • ESP32 displays may render colors differently
    • Test on actual hardware, not just PC
    • Adjust palette colors if needed
    • Consider display calibration

    Dual Palette Not Working

    • Ensure enableDualPaletteMode() is called first
    • Verify both palettes are set
    • Check that you're drawing in correct context
    • Review renderer documentation

    Next Steps

    Now that you understand palettes, learn about: - Cameras and Scrolling - Create scrolling levels - Tilemaps - Build levels with tiles - Particles and Effects - Add visual effects


    See also: - API Reference - PaletteDefs - API Reference - Color - Manual - Basic Rendering

    \ No newline at end of file +

    Troubleshooting

    Colors Not Changing

    • Verify setPalette() is called before drawing
    • Check palette is set in init(), not update()
    • Ensure dual palette mode is enabled if using separate palettes
    • Verify Color constants are from correct namespace

    Colors Look Wrong on Hardware

    • ESP32 displays may render colors differently
    • Test on actual hardware, not just PC
    • Adjust palette colors if needed
    • Consider display calibration

    Dual Palette Not Working

    • Ensure enableDualPaletteMode() is called first
    • Verify both palettes are set
    • Check that you're drawing in correct context
    • Review renderer documentation

    Next Steps

    Now that you understand palettes, learn about: - Cameras and Scrolling - Create scrolling levels - Tilemaps - Build levels with tiles - Particles and Effects - Add visual effects


    See also: - API Reference - PaletteDefs - API Reference - Color - Manual - Basic Rendering

    \ No newline at end of file diff --git a/site/manual/advanced_graphics/particles_and_effects/index.html b/site/manual/advanced_graphics/particles_and_effects/index.html index b909307..8552c06 100644 --- a/site/manual/advanced_graphics/particles_and_effects/index.html +++ b/site/manual/advanced_graphics/particles_and_effects/index.html @@ -1,4 +1,4 @@ - Particles and Effects - PixelRoot32 Documentation

    Particles and Effects

    The particle system allows you to create visual effects like fire, explosions, smoke, and sparks. This guide covers ParticleEmitter, ParticleConfig, and the included presets.

    ParticleEmitter Basics

    A ParticleEmitter is an Entity that manages a pool of particles to create visual effects.

    Creating a Particle Emitter

    #include <graphics/particles/ParticleEmitter.h>
    + Particles and Effects - PixelRoot32 Documentation      

    Particles and Effects

    The particle system allows you to create visual effects like fire, explosions, smoke, and sparks. This guide covers ParticleEmitter, ParticleConfig, and the included presets.

    ParticleEmitter Basics

    A ParticleEmitter is an Entity that manages a pool of particles to create visual effects.

    Creating a Particle Emitter

    #include <graphics/particles/ParticleEmitter.h>
     #include <graphics/particles/ParticleConfig.h>
     
     // Create particle configuration
    @@ -287,4 +287,4 @@
             emitter->update(deltaTime);
         }
     };
    -

    Troubleshooting

    Particles Not Appearing

    • Verify emitter is added to scene
    • Check particle config is valid
    • Ensure burst() is being called
    • Verify emitter position is on-screen

    Performance Issues

    • Reduce particle count per burst
    • Limit number of active emitters
    • Use simpler particle configs
    • Disable emitters when not visible

    Particles Not Moving

    • Check gravity value (positive = down, negative = up)
    • Verify speed is not 0
    • Check friction isn't too high (1.0 = no movement)
    • Ensure direction is correct (degrees: 0=right, 90=up, 180=left, 270=down)

    Next Steps

    Now that you understand particles, you've completed the advanced graphics section. Continue with: - Performance Optimization - Optimize your game - Memory Management - Manage memory efficiently - API Reference - Complete API documentation


    See also: - API Reference - ParticleEmitter - API Reference - ParticleConfig - API Reference - ParticlePresets - Manual - Basic Rendering

    \ No newline at end of file +

    Troubleshooting

    Particles Not Appearing

    • Verify emitter is added to scene
    • Check particle config is valid
    • Ensure burst() is being called
    • Verify emitter position is on-screen

    Performance Issues

    • Reduce particle count per burst
    • Limit number of active emitters
    • Use simpler particle configs
    • Disable emitters when not visible

    Particles Not Moving

    • Check gravity value (positive = down, negative = up)
    • Verify speed is not 0
    • Check friction isn't too high (1.0 = no movement)
    • Ensure direction is correct (degrees: 0=right, 90=up, 180=left, 270=down)

    Next Steps

    Now that you understand particles, you've completed the advanced graphics section. Continue with: - Performance Optimization - Optimize your game - Memory Management - Manage memory efficiently - API Reference - Complete API documentation


    See also: - API Reference - ParticleEmitter - API Reference - ParticleConfig - API Reference - ParticlePresets - Manual - Basic Rendering

    \ No newline at end of file diff --git a/site/manual/advanced_graphics/sprites_and_animation/index.html b/site/manual/advanced_graphics/sprites_and_animation/index.html index 06b1e57..f306e2e 100644 --- a/site/manual/advanced_graphics/sprites_and_animation/index.html +++ b/site/manual/advanced_graphics/sprites_and_animation/index.html @@ -1,4 +1,4 @@ - Sprites and Animation - PixelRoot32 Documentation

    Sprites and Animation

    This guide covers advanced sprite techniques and animation in PixelRoot32, including different sprite formats, creating animations, and best practices.

    Sprite Formats

    PixelRoot32 supports multiple sprite formats, each optimized for different use cases.

    1bpp (Standard, Monochrome)

    The standard format uses 1 bit per pixel (monochrome). This is the most memory-efficient format:

    #include <graphics/Renderer.h>
    + Sprites and Animation - PixelRoot32 Documentation      

    Sprites and Animation

    This guide covers advanced sprite techniques and animation in PixelRoot32, including different sprite formats, creating animations, and best practices.

    Sprite Formats

    PixelRoot32 supports multiple sprite formats, each optimized for different use cases.

    1bpp (Standard, Monochrome)

    The standard format uses 1 bit per pixel (monochrome). This is the most memory-efficient format:

    #include <graphics/Renderer.h>
     
     // Define sprite data (8x8 example)
     static const uint16_t PLAYER_SPRITE_DATA[] = {
    @@ -350,4 +350,4 @@
             return nullptr;
         }
     };
    -

    Troubleshooting

    Sprites Not Displaying

    • Check sprite data is valid
    • Verify width/height match data
    • Ensure sprite is within screen bounds
    • Check render layer is correct

    Animation Not Playing

    • Verify animation frames are set
    • Check step() is being called
    • Ensure timer logic is correct
    • Verify frame count matches array size

    Memory Issues

    • Reduce sprite count
    • Use 1bpp instead of 2bpp/4bpp
    • Reuse sprites more
    • Check available flash memory

    Next Steps

    Now that you understand sprites and animation, learn about: - Color Palettes - Use different color schemes - Cameras and Scrolling - Create scrolling levels - Tilemaps - Build levels with tiles


    See also: - API Reference - Sprite - API Reference - SpriteAnimation - Manual - Basic Rendering

    \ No newline at end of file +

    Troubleshooting

    Sprites Not Displaying

    • Check sprite data is valid
    • Verify width/height match data
    • Ensure sprite is within screen bounds
    • Check render layer is correct

    Animation Not Playing

    • Verify animation frames are set
    • Check step() is being called
    • Ensure timer logic is correct
    • Verify frame count matches array size

    Memory Issues

    • Reduce sprite count
    • Use 1bpp instead of 2bpp/4bpp
    • Reuse sprites more
    • Check available flash memory

    Next Steps

    Now that you understand sprites and animation, learn about: - Color Palettes - Use different color schemes - Cameras and Scrolling - Create scrolling levels - Tilemaps - Build levels with tiles


    See also: - API Reference - Sprite - API Reference - SpriteAnimation - Manual - Basic Rendering

    \ No newline at end of file diff --git a/site/manual/advanced_graphics/tilemaps/index.html b/site/manual/advanced_graphics/tilemaps/index.html index 9af63a3..7fc2e3e 100644 --- a/site/manual/advanced_graphics/tilemaps/index.html +++ b/site/manual/advanced_graphics/tilemaps/index.html @@ -1,4 +1,4 @@ - Tilemaps - PixelRoot32 Documentation

    Tilemaps

    Tilemaps allow you to build levels efficiently by reusing small tile sprites. This guide covers creating tilemaps, rendering them, and using them with scrolling cameras.

    What are Tilemaps?

    A tilemap is a 2D grid where each cell references a tile sprite. Instead of placing individual sprites, you define which tile appears at each grid position.

    Advantages: - Memory efficient: Reuse tile sprites many times - Easy level design: Edit level data, not code - Fast rendering: Optimized tilemap drawing - Large levels: Create levels bigger than screen - Multiple Bit-Depths: Support for 1bpp, 2bpp, and 4bpp tilemaps for higher graphical fidelity

    Creating a Tilemap

    1. Define Tiles

    First, create the tile sprites you'll reuse. You can use standard 1bpp sprites or multi-bpp sprites (2bpp/4bpp) if enabled.

    1bpp Tiles Example

    #include <graphics/Renderer.h>
    + Tilemaps - PixelRoot32 Documentation      

    Tilemaps

    Tilemaps allow you to build levels efficiently by reusing small tile sprites. This guide covers creating tilemaps, rendering them, and using them with scrolling cameras.

    What are Tilemaps?

    A tilemap is a 2D grid where each cell references a tile sprite. Instead of placing individual sprites, you define which tile appears at each grid position.

    Advantages: - Memory efficient: Reuse tile sprites many times - Easy level design: Edit level data, not code - Fast rendering: Optimized tilemap drawing - Large levels: Create levels bigger than screen - Multiple Bit-Depths: Support for 1bpp, 2bpp, and 4bpp tilemaps for higher graphical fidelity

    Creating a Tilemap

    1. Define Tiles

    First, create the tile sprites you'll reuse. You can use standard 1bpp sprites or multi-bpp sprites (2bpp/4bpp) if enabled.

    1bpp Tiles Example

    #include <graphics/Renderer.h>
     
     // Ground tile (solid)
     static const uint16_t TILE_GROUND_BITS[] = {
    @@ -297,4 +297,4 @@
     
         return false; // No collision
     }
    -

    Troubleshooting

    Tiles Not Appearing

    • Verify tile indices are correct (0 = first tile, 1 = second, etc.)
    • Check tilemap dimensions match indices array size
    • Ensure tiles array has enough entries
    • Verify tile size matches sprite size

    Performance Issues

    • Reduce tilemap size
    • Use smaller tiles
    • Limit number of unique tiles
    • Test viewport culling

    Scrolling Problems

    • Ensure camera is applied before drawing tilemap
    • Check tilemap position matches camera offset
    • Verify tilemap boundaries are correct
    • Test with simple tilemap first

    Next Steps

    Now that you understand tilemaps, learn about: - Particles and Effects - Add visual effects - Cameras and Scrolling - Combine with scrolling - Performance Optimization - Optimize rendering


    See also: - API Reference - TileMap - API Reference - Renderer - Manual - Cameras and Scrolling

    \ No newline at end of file +

    Troubleshooting

    Tiles Not Appearing

    • Verify tile indices are correct (0 = first tile, 1 = second, etc.)
    • Check tilemap dimensions match indices array size
    • Ensure tiles array has enough entries
    • Verify tile size matches sprite size

    Performance Issues

    • Reduce tilemap size
    • Use smaller tiles
    • Limit number of unique tiles
    • Test viewport culling

    Scrolling Problems

    • Ensure camera is applied before drawing tilemap
    • Check tilemap position matches camera offset
    • Verify tilemap boundaries are correct
    • Test with simple tilemap first

    Next Steps

    Now that you understand tilemaps, learn about: - Particles and Effects - Add visual effects - Cameras and Scrolling - Combine with scrolling - Performance Optimization - Optimize rendering


    See also: - API Reference - TileMap - API Reference - Renderer - Manual - Cameras and Scrolling

    \ No newline at end of file diff --git a/site/manual/game_development/audio/index.html b/site/manual/game_development/audio/index.html index 420c17c..7e86b21 100644 --- a/site/manual/game_development/audio/index.html +++ b/site/manual/game_development/audio/index.html @@ -1,4 +1,4 @@ - Audio - PixelRoot32 Documentation

    Audio

    PixelRoot32 includes a complete NES-like audio system with 4 channels for sound effects and background music. This guide shows you how to add sound and music to your games.

    Audio Configuration

    Before using audio, you need to configure an AudioBackend. This is done when creating the Engine:

    ESP32: Internal DAC

    #include <drivers/esp32/ESP32_DAC_AudioBackend.h>
    + Audio - PixelRoot32 Documentation      

    Audio

    PixelRoot32 includes a complete NES-like audio system with 4 channels for sound effects and background music. This guide shows you how to add sound and music to your games.

    Audio Configuration

    Before using audio, you need to configure an AudioBackend. This is done when creating the Engine:

    ESP32: Internal DAC

    #include <drivers/esp32/ESP32_DAC_AudioBackend.h>
     
     const int DAC_PIN = 25; // GPIO 25 or 26
     pr32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(DAC_PIN, 11025);
    @@ -234,4 +234,4 @@
             Scene::update(deltaTime);
         }
     };
    -

    Troubleshooting

    No Sound

    • Check audio backend is configured correctly
    • Verify sample rate matches backend
    • Check master volume is not 0
    • Ensure audio is initialized (engine.init())

    Distorted Sound

    • Lower volume levels
    • Reduce sample rate (ESP32 DAC works better at 11025 Hz)
    • Check for too many simultaneous sounds
    • Verify hardware connections (ESP32)

    Music Not Playing

    • Check music.isPlaying() status
    • Ensure track is properly defined
    • Verify MusicPlayer is updated (happens automatically)
    • Check that music channel is not being used by SFX

    Next Steps

    Now that you can add audio, learn about: - NES Audio Reference - Advanced audio techniques - Physics and Collisions - Make objects interact - User Interface - Create menus and HUDs


    See also: - API Reference - AudioEngine - API Reference - MusicPlayer - API Reference - Audio Types - Manual - Audio Overview

    \ No newline at end of file +

    Troubleshooting

    No Sound

    • Check audio backend is configured correctly
    • Verify sample rate matches backend
    • Check master volume is not 0
    • Ensure audio is initialized (engine.init())

    Distorted Sound

    • Lower volume levels
    • Reduce sample rate (ESP32 DAC works better at 11025 Hz)
    • Check for too many simultaneous sounds
    • Verify hardware connections (ESP32)

    Music Not Playing

    • Check music.isPlaying() status
    • Ensure track is properly defined
    • Verify MusicPlayer is updated (happens automatically)
    • Check that music channel is not being used by SFX

    Next Steps

    Now that you can add audio, learn about: - NES Audio Reference - Advanced audio techniques - Physics and Collisions - Make objects interact - User Interface - Create menus and HUDs


    See also: - API Reference - AudioEngine - API Reference - MusicPlayer - API Reference - Audio Types - Manual - Audio Overview

    \ No newline at end of file diff --git a/site/manual/game_development/basic_rendering/index.html b/site/manual/game_development/basic_rendering/index.html index 30c02f4..20a801a 100644 --- a/site/manual/game_development/basic_rendering/index.html +++ b/site/manual/game_development/basic_rendering/index.html @@ -1,4 +1,4 @@ - Basic Rendering - PixelRoot32 Documentation

    Basic Rendering

    Rendering is how you draw everything on screen. This guide covers the fundamental drawing operations in PixelRoot32: primitives, sprites, and text.

    Accessing the Renderer

    You can access the renderer in two ways:

    From the Engine

    auto& renderer = engine.getRenderer();
    + Basic Rendering - PixelRoot32 Documentation      

    Basic Rendering

    Rendering is how you draw everything on screen. This guide covers the fundamental drawing operations in PixelRoot32: primitives, sprites, and text.

    Accessing the Renderer

    You can access the renderer in two ways:

    From the Engine

    auto& renderer = engine.getRenderer();
     

    From a Scene

    void MyScene::draw(pixelroot32::graphics::Renderer& renderer) override {
         // renderer is passed as parameter
         renderer.drawFilledRectangle(0, 0, 240, 240, Color::Black);
    @@ -140,4 +140,4 @@
             renderer.drawSprite(sprite, x, startY, Color::White);
         }
     }
    -

    Next Steps

    Now that you can draw basic graphics, learn about: - Sprites and Animation - Advanced sprite techniques - Input and Control - Make your game interactive - Palettes - Use different color schemes


    See also: - API Reference - Renderer - API Reference - Sprite - Render Layers

    \ No newline at end of file +

    Next Steps

    Now that you can draw basic graphics, learn about: - Sprites and Animation - Advanced sprite techniques - Input and Control - Make your game interactive - Palettes - Use different color schemes


    See also: - API Reference - Renderer - API Reference - Sprite - Render Layers

    \ No newline at end of file diff --git a/site/manual/game_development/input_and_control/index.html b/site/manual/game_development/input_and_control/index.html index c0eb4af..f864c2f 100644 --- a/site/manual/game_development/input_and_control/index.html +++ b/site/manual/game_development/input_and_control/index.html @@ -1,4 +1,4 @@ - Input and Control - PixelRoot32 Documentation

    Input and Control

    Handling user input is essential for any interactive game. This guide covers how to read and process input from buttons (ESP32) or keyboard (Native) in PixelRoot32.

    Input Configuration

    Before you can read input, you need to configure the InputManager. This is done when creating the Engine:

    ESP32 Configuration

    #include <input/InputConfig.h>
    + Input and Control - PixelRoot32 Documentation      

    Input and Control

    Handling user input is essential for any interactive game. This guide covers how to read and process input from buttons (ESP32) or keyboard (Native) in PixelRoot32.

    Input Configuration

    Before you can read input, you need to configure the InputManager. This is done when creating the Engine:

    ESP32 Configuration

    #include <input/InputConfig.h>
     
     // InputConfig(buttonCount, UP, DOWN, LEFT, RIGHT, A, B)
     pr32::input::InputConfig inputConfig(
    @@ -258,4 +258,4 @@
         if (input.isButtonReleased(button)) return InputState::RELEASED;
         return InputState::IDLE;
     }
    -

    Troubleshooting

    Button Not Responding

    • Check button indices match your InputConfig
    • Verify GPIO pins (ESP32) or scancodes (Native) are correct
    • Ensure InputManager is being updated (happens automatically in Engine)

    Input Feels Laggy

    • Ensure you're using deltaTime for movement
    • Check that input is read in update(), not draw()
    • Verify framerate is stable

    Multiple Triggers

    • Use isButtonPressed() instead of isButtonDown() for one-time actions
    • Implement input buffering or cooldown timers

    Next Steps

    Now that you can handle input, learn about: - Audio - Add sound effects and music - Physics and Collisions - Make objects interact - User Interface - Create menus and HUDs


    See also: - API Reference - InputManager - API Reference - InputConfig - Manual - Input Overview

    \ No newline at end of file +

    Troubleshooting

    Button Not Responding

    • Check button indices match your InputConfig
    • Verify GPIO pins (ESP32) or scancodes (Native) are correct
    • Ensure InputManager is being updated (happens automatically in Engine)

    Input Feels Laggy

    • Ensure you're using deltaTime for movement
    • Check that input is read in update(), not draw()
    • Verify framerate is stable

    Multiple Triggers

    • Use isButtonPressed() instead of isButtonDown() for one-time actions
    • Implement input buffering or cooldown timers

    Next Steps

    Now that you can handle input, learn about: - Audio - Add sound effects and music - Physics and Collisions - Make objects interact - User Interface - Create menus and HUDs


    See also: - API Reference - InputManager - API Reference - InputConfig - Manual - Input Overview

    \ No newline at end of file diff --git a/site/manual/game_development/physics_and_collisions/index.html b/site/manual/game_development/physics_and_collisions/index.html index a06ef43..cdc7def 100644 --- a/site/manual/game_development/physics_and_collisions/index.html +++ b/site/manual/game_development/physics_and_collisions/index.html @@ -1,4 +1,4 @@ - Physics and Collisions - PixelRoot32 Documentation

    Physics and Collisions

    PixelRoot32 provides a physics system for moving objects and collision detection. This guide covers PhysicsActor for automatic physics and the collision system for detecting interactions between objects.

    PhysicsActor

    A PhysicsActor is an Actor that automatically handles physics: velocity, gravity, friction, and world boundary collisions.

    Creating a PhysicsActor

    #include <core/PhysicsActor.h>
    + Physics and Collisions - PixelRoot32 Documentation      

    Physics and Collisions

    PixelRoot32 provides a physics system for moving objects and collision detection. This guide covers PhysicsActor for automatic physics and the collision system for detecting interactions between objects.

    PhysicsActor

    A PhysicsActor is an Actor that automatically handles physics: velocity, gravity, friction, and world boundary collisions.

    Creating a PhysicsActor

    #include <core/PhysicsActor.h>
     
     class Ball : public pixelroot32::core::PhysicsActor {
     public:
    @@ -323,4 +323,4 @@
             }
         }
     }
    -

    Troubleshooting

    Collisions Not Detected

    • Verify collision layers and masks overlap
    • Check that actors are added to the scene
    • Ensure Scene::update() is called
    • Verify hitboxes are correct

    Physics Too Fast/Slow

    • Adjust values based on deltaTime
    • Check gravity and velocity values
    • Test on actual hardware (ESP32 may run slower)

    Objects Passing Through

    • Use sweep tests for fast objects
    • Increase collision detection frequency
    • Check hitbox sizes match visual size

    Next Steps

    Now that you understand physics and collisions, learn about: - User Interface - Create menus and HUDs - Advanced Graphics - Advanced sprite techniques - Camera and Scrolling - Create scrolling levels


    See also: - API Reference - PhysicsActor - API Reference - CollisionSystem - Manual - Physics Overview - Manual - Collision Detection

    \ No newline at end of file +

    Troubleshooting

    Collisions Not Detected

    • Verify collision layers and masks overlap
    • Check that actors are added to the scene
    • Ensure Scene::update() is called
    • Verify hitboxes are correct

    Physics Too Fast/Slow

    • Adjust values based on deltaTime
    • Check gravity and velocity values
    • Test on actual hardware (ESP32 may run slower)

    Objects Passing Through

    • Use sweep tests for fast objects
    • Increase collision detection frequency
    • Check hitbox sizes match visual size

    Next Steps

    Now that you understand physics and collisions, learn about: - User Interface - Create menus and HUDs - Advanced Graphics - Advanced sprite techniques - Camera and Scrolling - Create scrolling levels


    See also: - API Reference - PhysicsActor - API Reference - CollisionSystem - Manual - Physics Overview - Manual - Collision Detection

    \ No newline at end of file diff --git a/site/manual/game_development/scenes_and_entities/index.html b/site/manual/game_development/scenes_and_entities/index.html index 64d24d1..32ba43e 100644 --- a/site/manual/game_development/scenes_and_entities/index.html +++ b/site/manual/game_development/scenes_and_entities/index.html @@ -1,4 +1,4 @@ - Scenes and Entities - PixelRoot32 Documentation

    Scenes and Entities

    Scenes and entities are the foundation of every PixelRoot32 game. This guide teaches you how to organize your game using scenes and create interactive game objects with entities.

    Creating a Scene

    A Scene represents a screen or level in your game. To create a scene, inherit from pixelroot32::core::Scene and implement the three main methods:

    #include <core/Scene.h>
    + Scenes and Entities - PixelRoot32 Documentation      

    Scenes and Entities

    Scenes and entities are the foundation of every PixelRoot32 game. This guide teaches you how to organize your game using scenes and create interactive game objects with entities.

    Creating a Scene

    A Scene represents a screen or level in your game. To create a scene, inherit from pixelroot32::core::Scene and implement the three main methods:

    #include <core/Scene.h>
     #include <graphics/Renderer.h>
     
     class MyGameScene : public pixelroot32::core::Scene {
    @@ -254,4 +254,4 @@
             // ...
         }
     }
    -

    Next Steps

    Now that you understand scenes and entities, learn about: - Basic Rendering - Draw sprites, text, and primitives - Input and Control - Handle user input - Physics and Collisions - Advanced collision handling


    See also: - Fundamental Concepts - API Reference - Scene - API Reference - Entity - API Reference - Actor

    \ No newline at end of file +

    Next Steps

    Now that you understand scenes and entities, learn about: - Basic Rendering - Draw sprites, text, and primitives - Input and Control - Handle user input - Physics and Collisions - Advanced collision handling


    See also: - Fundamental Concepts - API Reference - Scene - API Reference - Entity - API Reference - Actor

    \ No newline at end of file diff --git a/site/manual/game_development/user_interface/index.html b/site/manual/game_development/user_interface/index.html index 7d9e6d0..4aff183 100644 --- a/site/manual/game_development/user_interface/index.html +++ b/site/manual/game_development/user_interface/index.html @@ -1,4 +1,4 @@ - User Interface - PixelRoot32 Documentation

    User Interface

    PixelRoot32 provides a complete UI system for creating menus, HUDs, and interface elements. This guide covers all UI components and layout systems.

    UI Elements

    All UI elements inherit from UIElement, which itself inherits from Entity. This means UI elements can be added to scenes just like any other entity.

    UILabel

    Display text on screen:

    #include <graphics/ui/UILabel.h>
    + User Interface - PixelRoot32 Documentation      

    User Interface

    PixelRoot32 provides a complete UI system for creating menus, HUDs, and interface elements. This guide covers all UI components and layout systems.

    UI Elements

    All UI elements inherit from UIElement, which itself inherits from Entity. This means UI elements can be added to scenes just like any other entity.

    UILabel

    Display text on screen:

    #include <graphics/ui/UILabel.h>
     
     // Create a label
     pixelroot32::graphics::ui::UILabel* scoreLabel = new pixelroot32::graphics::ui::UILabel(
    @@ -389,4 +389,4 @@
         snprintf(buffer, sizeof(buffer), "Health: %d%%", health);
         healthLabel->setText(buffer);
     }
    -

    Next Steps

    Now that you understand the UI system, you've completed the core game development topics. Continue with: - Advanced Graphics - Advanced sprite techniques - Camera and Scrolling - Create scrolling levels - Performance Tuning - Improve performance


    See also: - API Reference - UIElement - API Reference - UIButton - API Reference - UI Layouts - Manual - UI Overview

    \ No newline at end of file +

    Next Steps

    Now that you understand the UI system, you've completed the core game development topics. Continue with: - Advanced Graphics - Advanced sprite techniques - Camera and Scrolling - Create scrolling levels - Performance Tuning - Improve performance


    See also: - API Reference - UIElement - API Reference - UIButton - API Reference - UI Layouts - Manual - UI Overview

    \ No newline at end of file diff --git a/site/manual/optimization/extensibility/index.html b/site/manual/optimization/extensibility/index.html index 5cac74b..06e9166 100644 --- a/site/manual/optimization/extensibility/index.html +++ b/site/manual/optimization/extensibility/index.html @@ -1,4 +1,4 @@ - Extensibility - PixelRoot32 Documentation

    Extensibility

    PixelRoot32 is designed to be extensible. This guide covers how to create custom drivers, audio backends, and extend existing systems.

    Creating Custom Display Drivers

    To support a new display, implement the DrawSurface interface.

    DrawSurface Interface

    #include <graphics/DrawSurface.h>
    + Extensibility - PixelRoot32 Documentation      

    Extensibility

    PixelRoot32 is designed to be extensible. This guide covers how to create custom drivers, audio backends, and extend existing systems.

    Creating Custom Display Drivers

    To support a new display, implement the DrawSurface interface.

    DrawSurface Interface

    #include <graphics/DrawSurface.h>
     
     class MyCustomDrawer : public pixelroot32::graphics::DrawSurface {
     public:
    @@ -348,4 +348,4 @@
             }
         }
     };
    -

    Troubleshooting

    Driver Not Working

    • Verify all interface methods are implemented
    • Check initialization order
    • Test with simple drawing first
    • Verify hardware connections

    Audio Backend Issues

    • Check sample rate matches hardware
    • Verify audio generation logic
    • Test with simple tones first
    • Check channel management

    Extension Conflicts

    • Ensure namespace isolation
    • Avoid modifying engine code directly
    • Use composition over modification
    • Test with engine updates

    Next Steps

    Now that you understand extensibility, you've completed the optimization section. Continue with: - API Reference - Complete API documentation - Examples - Code examples - Resources - Tools and troubleshooting


    See also: - API Reference - DrawSurface - API Reference - AudioBackend - Manual - Platforms and Drivers

    \ No newline at end of file +

    Troubleshooting

    Driver Not Working

    • Verify all interface methods are implemented
    • Check initialization order
    • Test with simple drawing first
    • Verify hardware connections

    Audio Backend Issues

    • Check sample rate matches hardware
    • Verify audio generation logic
    • Test with simple tones first
    • Check channel management

    Extension Conflicts

    • Ensure namespace isolation
    • Avoid modifying engine code directly
    • Use composition over modification
    • Test with engine updates

    Next Steps

    Now that you understand extensibility, you've completed the optimization section. Continue with: - API Reference - Complete API documentation - Examples - Code examples - Resources - Tools and troubleshooting


    See also: - API Reference - DrawSurface - API Reference - AudioBackend - Manual - Platforms and Drivers

    \ No newline at end of file diff --git a/site/manual/optimization/memory_management/index.html b/site/manual/optimization/memory_management/index.html index c0478ad..8d91845 100644 --- a/site/manual/optimization/memory_management/index.html +++ b/site/manual/optimization/memory_management/index.html @@ -1,4 +1,4 @@ - Memory Management - PixelRoot32 Documentation

    Memory Management

    ESP32 has limited memory, so efficient memory management is crucial for PixelRoot32 games. This guide covers memory constraints, object pooling, and best practices.

    ESP32 Memory Constraints

    Available Memory

    ESP32 typically has: - RAM: ~320KB total (varies by model) - Flash: 4MB+ (for program storage) - Heap: Limited and fragmented over time

    Real-World Limits

    • MAX_ENTITIES: 32 per scene (hard limit)
    • Sprite data: Stored in flash (const/constexpr)
    • Dynamic allocation: Should be avoided in game loop
    • Stack: Limited (~8KB), avoid large stack allocations

    Object Pooling

    Object pooling reuses objects instead of creating/destroying them, avoiding memory fragmentation.

    Basic Pool Pattern

    class ProjectilePool {
    + Memory Management - PixelRoot32 Documentation      

    Memory Management

    ESP32 has limited memory, so efficient memory management is crucial for PixelRoot32 games. This guide covers memory constraints, object pooling, and best practices.

    ESP32 Memory Constraints

    Available Memory

    ESP32 typically has: - RAM: ~320KB total (varies by model) - Flash: 4MB+ (for program storage) - Heap: Limited and fragmented over time

    Real-World Limits

    • MAX_ENTITIES: 32 per scene (hard limit)
    • Sprite data: Stored in flash (const/constexpr)
    • Dynamic allocation: Should be avoided in game loop
    • Stack: Limited (~8KB), avoid large stack allocations

    Object Pooling

    Object pooling reuses objects instead of creating/destroying them, avoiding memory fragmentation.

    Basic Pool Pattern

    class ProjectilePool {
     private:
         static const int POOL_SIZE = 10;
         ProjectileActor pool[POOL_SIZE];
    @@ -294,4 +294,4 @@
         int size() const { return count; }
         pixelroot32::core::Entity* operator[](int index) { return entities[index]; }
     };
    -

    Troubleshooting

    Out of Memory Errors

    • Reduce pool sizes
    • Use fewer entities
    • Store more data in flash
    • Avoid dynamic allocation
    • Check for memory leaks

    Entity Limit Reached

    • MAX_ENTITIES = 32 is a hard limit
    • Use object pooling to reuse entities
    • Deactivate entities instead of removing
    • Combine multiple entities into one

    Memory Fragmentation

    • Use object pooling
    • Pre-allocate all resources
    • Avoid frequent new/delete
    • Consider Scene Arena (experimental)

    Next Steps

    Now that you understand memory management, learn about: - Performance Optimization - Improve game performance - Platforms and Drivers - Understand platform specifics - Extensibility - Extend the engine


    See also: - API Reference - Scene - Manual - Scenes and Entities

    \ No newline at end of file +

    Troubleshooting

    Out of Memory Errors

    • Reduce pool sizes
    • Use fewer entities
    • Store more data in flash
    • Avoid dynamic allocation
    • Check for memory leaks

    Entity Limit Reached

    • MAX_ENTITIES = 32 is a hard limit
    • Use object pooling to reuse entities
    • Deactivate entities instead of removing
    • Combine multiple entities into one

    Memory Fragmentation

    • Use object pooling
    • Pre-allocate all resources
    • Avoid frequent new/delete
    • Consider Scene Arena (experimental)

    Next Steps

    Now that you understand memory management, learn about: - Performance Optimization - Improve game performance - Platforms and Drivers - Understand platform specifics - Extensibility - Extend the engine


    See also: - API Reference - Scene - Manual - Scenes and Entities

    \ No newline at end of file diff --git a/site/manual/optimization/performance_tuning/index.html b/site/manual/optimization/performance_tuning/index.html index d2b4c7f..fb2245e 100644 --- a/site/manual/optimization/performance_tuning/index.html +++ b/site/manual/optimization/performance_tuning/index.html @@ -1,4 +1,4 @@ - Performance Tuning - PixelRoot32 Documentation

    Performance Optimization

    This guide covers techniques to improve game performance on ESP32, including rendering optimization, logic optimization, and profiling.

    ESP32 Performance Characteristics

    CPU Limitations

    • Dual-core: 240MHz (typically)
    • Single-threaded game loop: One core handles everything
    • Target FPS: 30-60 FPS (depends on game complexity)
    • Frame budget: ~16-33ms per frame at 60 FPS

    Common Bottlenecks

    1. Rendering: Too many draw calls
    2. Collision detection: Too many collision checks
    3. Memory allocation: Dynamic allocation in game loop
    4. Complex calculations: Expensive math operations
    5. String operations: String concatenation/formatting

    Rendering Optimization

    Viewport Culling

    Only draw entities that are visible on screen:

    bool isOnScreen(float x, float y, int width, int height, 
    + Performance Tuning - PixelRoot32 Documentation      

    Performance Optimization

    This guide covers techniques to improve game performance on ESP32, including rendering optimization, logic optimization, and profiling.

    ESP32 Performance Characteristics

    CPU Limitations

    • Dual-core: 240MHz (typically)
    • Single-threaded game loop: One core handles everything
    • Target FPS: 30-60 FPS (depends on game complexity)
    • Frame budget: ~16-33ms per frame at 60 FPS

    Common Bottlenecks

    1. Rendering: Too many draw calls
    2. Collision detection: Too many collision checks
    3. Memory allocation: Dynamic allocation in game loop
    4. Complex calculations: Expensive math operations
    5. String operations: String concatenation/formatting

    Técnicas de Optimización

    El motor utiliza varias técnicas para maximizar los FPS, especialmente en hardware limitado como el ESP32.

    1. Viewport Culling (Recorte de Cámara)

    No proceses objetos que están fuera de la pantalla. El motor lo hace automáticamente en drawTileMap, pero debes implementarlo en tu lógica de actualización:

    bool isOnScreen(float x, float y, int width, int height, 
                     const Camera2D& camera) {
         float cameraX = camera.getX();
         float cameraY = camera.getY();
    @@ -10,285 +10,58 @@
                  y + height < cameraY || 
                  y > cameraY + screenHeight);
     }
    -
    -void draw(pixelroot32::graphics::Renderer& renderer) override {
    -    camera.apply(renderer);
    -
    -    // Only draw visible entities
    -    for (auto* entity : entities) {
    -        if (entity->isVisible && 
    -            isOnScreen(entity->x, entity->y, entity->width, entity->height, camera)) {
    -            entity->draw(renderer);
    -        }
    -    }
    -}
    -

    Reduce Draw Calls

    Batch similar operations:

    // ❌ BAD: Many individual draw calls
    -void drawBackground(Renderer& renderer) {
    -    for (int y = 0; y < 30; y++) {
    -        for (int x = 0; x < 30; x++) {
    -            renderer.drawSprite(tile, x * 8, y * 8, Color::White);
    -        }
    -    }
    -}
    -
    -// ✅ GOOD: Use tilemap (single call)
    -void drawBackground(Renderer& renderer) {
    -    renderer.drawTileMap(backgroundTileMap, 0, 0, Color::White);
    -}
    -

    Optimize Sprite Drawing

    • Reuse sprites: Define once, use many times
    • Use 1bpp: Most efficient format
    • Limit sprite size: Smaller sprites = faster drawing
    • Avoid flipping: Flipping has overhead

    Efficient Render Layers

    // Organize by layer to minimize layer switches
    -void draw(pixelroot32::graphics::Renderer& renderer) override {
    -    // Draw all layer 0 entities
    -    for (auto* entity : layer0Entities) {
    -        if (entity->isVisible) entity->draw(renderer);
    -    }
    -
    -    // Draw all layer 1 entities
    -    for (auto* entity : layer1Entities) {
    -        if (entity->isVisible) entity->draw(renderer);
    -    }
    -
    -    // Draw all layer 2 entities
    -    for (auto* entity : layer2Entities) {
    -        if (entity->isVisible) entity->draw(renderer);
    -    }
    -}
    -

    Logic Optimization

    Reduce Calculations Per Frame

    Cache expensive calculations:

    class OptimizedActor : public pixelroot32::core::Actor {
    -private:
    -    float cachedDistance = 0.0f;
    -    bool distanceDirty = true;
    -
    -public:
    -    void update(unsigned long deltaTime) override {
    -        // Only recalculate when position changes
    -        if (distanceDirty) {
    -            cachedDistance = sqrt(x * x + y * y);
    -            distanceDirty = false;
    -        }
    -
    -        // Use cached value
    -        if (cachedDistance > maxDistance) {
    -            // ...
    +

    2. Optimización de Memoria y CPU (ESP32)

    Para la plataforma ESP32, se han implementado optimizaciones de bajo nivel críticas:

    • IRAM_ATTR: Las funciones críticas de renderizado (drawSprite, drawTileMap, etc.) están marcadas para ejecutarse desde la RAM interna (IRAM), eliminando la latencia de lectura de la Flash SPI.
    • DMA (Direct Memory Access): El volcado del buffer a la pantalla TFT se realiza mediante DMA, lo que permite que la CPU comience a procesar el siguiente frame mientras el hardware transfiere los datos.
    • Acceso a Datos de 16 bits: Los sprites de 2bpp y 4bpp utilizan punteros uint16_t* para garantizar accesos alineados a memoria, lo cual es significativamente más rápido en la arquitectura Xtensa del ESP32.

    3. Optimización de TileMaps

    El renderizado de mapas de tiles es una de las operaciones más costosas. PixelRoot32 utiliza:

    • Caché de Paleta: Durante el dibujado de un tilemap, se genera una tabla de búsqueda (LUT) temporal para evitar cálculos de color redundantes por cada píxel.
    • Dibujado por Columnas: Optimizado para minimizar los saltos de memoria en el framebuffer.

    4. Colisiones Eficientes

    Usa colisiones basadas en tiles siempre que sea posible. Acceder a un array de tiles es O(1), mientras que iterar sobre una lista de entidades es O(n).

    // Ejemplo de colisión rápida con el mapa
    +int tileX = x / 8;
    +int tileY = y / 8;
    +if (levelMap.data[tileY * levelMap.width + tileX] != 0) {
    +    // Colisión detectada
    +}
    +

    Recomendaciones Generales

    • Sprites Indexados: Prefiere Sprite2bpp (4 colores) o Sprite4bpp (16 colores) sobre Sprite (1bpp) si necesitas color, ya que están altamente optimizados.
    • Evitar std::string en el Loop: Las concatenaciones de strings generan fragmentación de memoria. Usa buffers estáticos o char[] para textos dinámicos.
    • Perfilado: Utiliza engine.getFPS() para monitorear el impacto de tus cambios en tiempo real.

    Common Optimization Patterns

    Update Frequency Reduction

    class LowFrequencyUpdater {
    +private:
    +    unsigned long timer = 0;
    +    unsigned long interval = 100; // Update every 100ms
    +
    +public:
    +    void update(unsigned long deltaTime) {
    +        timer += deltaTime;
    +        if (timer >= interval) {
    +            timer -= interval;
    +            // Do expensive update
    +            expensiveUpdate();
    +        }
    +    }
    +};
    +

    Spatial Partitioning (Simple)

    // Divide screen into zones
    +class SpatialGrid {
    +private:
    +    static const int GRID_SIZE = 4;
    +    static const int CELL_WIDTH = 60;
    +    static const int CELL_HEIGHT = 60;
    +
    +    std::vector<Actor*> grid[GRID_SIZE][GRID_SIZE];
    +
    +public:
    +    void add(Actor* actor) {
    +        int cellX = static_cast<int>(actor->x) / CELL_WIDTH;
    +        int cellY = static_cast<int>(actor->y) / CELL_HEIGHT;
    +        if (cellX >= 0 && cellX < GRID_SIZE && 
    +            cellY >= 0 && cellY < GRID_SIZE) {
    +            grid[cellY][cellX].push_back(actor);
             }
         }
     
    -    void setPosition(float newX, float newY) {
    -        x = newX;
    -        y = newY;
    -        distanceDirty = true; // Mark for recalculation
    -    }
    -};
    -

    Lazy Evaluation

    Only calculate when needed:

    class LazyCalculator {
    -private:
    -    mutable bool cached = false;
    -    mutable float cachedValue = 0.0f;
    -
    -public:
    -    float getValue() const {
    -        if (!cached) {
    -            cachedValue = expensiveCalculation();
    -            cached = true;
    -        }
    -        return cachedValue;
    -    }
    -
    -    void invalidate() {
    -        cached = false;
    -    }
    -};
    -

    Avoid Expensive Operations

    // ❌ BAD: sqrt() every frame
    -float distance = sqrt(dx * dx + dy * dy);
    -
    -// ✅ GOOD: Use squared distance
    -float distanceSq = dx * dx + dy * dy;
    -if (distanceSq > maxDistanceSq) {
    -    // ...
    -}
    -
    -// ❌ BAD: sin/cos every frame
    -float x = cos(angle) * radius;
    -float y = sin(angle) * radius;
    -
    -// ✅ GOOD: Pre-calculate or use lookup table
    -static const float COS_TABLE[360] = { /* ... */ };
    -static const float SIN_TABLE[360] = { /* ... */ };
    -float x = COS_TABLE[static_cast<int>(angle) % 360] * radius;
    -

    Collision Optimization

    Use Collision Layers Efficiently

    // ❌ BAD: Check everything against everything
    -for (auto* actor1 : actors) {
    -    for (auto* actor2 : actors) {
    -        if (actor1 != actor2) {
    -            checkCollision(actor1, actor2);
    -        }
    -    }
    -}
    -
    -// ✅ GOOD: Use layers to reduce checks
    -// CollisionSystem automatically uses layers
    -// Only actors with matching layers/masks are checked
    -

    Reduce Collision Checks

    // Only check collisions for active actors
    -void update(unsigned long deltaTime) override {
    -    for (auto* actor : actors) {
    -        if (actor->isEnabled && actor->isActive) {
    -            actor->update(deltaTime);
    -        }
    -    }
    -
    -    // CollisionSystem only checks enabled actors
    -    Scene::update(deltaTime);
    -}
    -

    Simple Hitboxes

    // ✅ GOOD: Simple AABB
    -pixelroot32::core::Rect getHitBox() override {
    -    return {x, y, width, height};
    -}
    -
    -// ❌ BAD: Complex shape calculations
    -pixelroot32::core::Rect getHitBox() override {
    -    // Complex polygon calculations...
    -}
    -

    String Optimization

    Avoid String Operations

    // ❌ BAD: String concatenation
    -std::string text = "Score: " + std::to_string(score);
    -
    -// ✅ GOOD: Static buffer with snprintf
    -char buffer[32];
    -snprintf(buffer, sizeof(buffer), "Score: %d", score);
    -renderer.drawText(buffer, 10, 10, Color::White, 1);
    -

    Cache Text Rendering

    class CachedText {
    -private:
    -    char buffer[32];
    -    bool dirty = true;
    -    int lastValue = -1;
    -
    -public:
    -    void update(int value) {
    -        if (value != lastValue) {
    -            snprintf(buffer, sizeof(buffer), "Score: %d", value);
    -            lastValue = value;
    -            dirty = true;
    -        }
    -    }
    -
    -    void draw(Renderer& renderer, int x, int y) {
    -        if (dirty) {
    -            renderer.drawText(buffer, x, y, Color::White, 1);
    -            dirty = false;
    -        }
    -    }
    -};
    -

    Profiling

    Measure Frame Time

    class PerformanceMonitor {
    -private:
    -    unsigned long frameTime = 0;
    -    unsigned long maxFrameTime = 0;
    -    unsigned long frameCount = 0;
    -
    -public:
    -    void startFrame() {
    -        frameTime = millis();
    -    }
    -
    -    void endFrame() {
    -        unsigned long elapsed = millis() - frameTime;
    -        if (elapsed > maxFrameTime) {
    -            maxFrameTime = elapsed;
    -        }
    -        frameCount++;
    -
    -        // Log every 60 frames
    -        if (frameCount % 60 == 0) {
    -            Serial.print("Max frame time: ");
    -            Serial.println(maxFrameTime);
    -            maxFrameTime = 0;
    -        }
    -    }
    -};
    -
    -// Usage
    -PerformanceMonitor perf;
    -
    -void update(unsigned long deltaTime) override {
    -    perf.startFrame();
    -    Scene::update(deltaTime);
    -    perf.endFrame();
    -}
    -

    Identify Bottlenecks

    #ifdef PLATFORM_ESP32
    -#include <Arduino.h>
    -
    -class Profiler {
    -private:
    -    unsigned long updateTime = 0;
    -    unsigned long drawTime = 0;
    -    unsigned long collisionTime = 0;
    -
    -public:
    -    void startUpdate() {
    -        updateTime = micros();
    -    }
    -
    -    void endUpdate() {
    -        updateTime = micros() - updateTime;
    -    }
    -
    -    void startDraw() {
    -        drawTime = micros();
    -    }
    -
    -    void endDraw() {
    -        drawTime = micros() - drawTime;
    -    }
    -
    -    void log() {
    -        Serial.print("Update: ");
    -        Serial.print(updateTime);
    -        Serial.print("us, Draw: ");
    -        Serial.print(drawTime);
    -        Serial.println("us");
    -    }
    -};
    -#endif
    -

    Best Practices Summary

    Rendering

    • ✅ Use viewport culling
    • ✅ Batch similar draw operations
    • ✅ Use tilemaps for backgrounds
    • ✅ Limit sprite count and size
    • ✅ Organize by render layers

    Logic

    • ✅ Cache expensive calculations
    • ✅ Use lazy evaluation
    • ✅ Avoid sqrt/sin/cos in loops
    • ✅ Pre-calculate lookup tables
    • ✅ Reduce update frequency for non-critical entities

    Memory

    • ✅ Use object pooling
    • ✅ Avoid dynamic allocation
    • ✅ Store data in flash
    • ✅ Reuse objects

    Collisions

    • ✅ Use collision layers efficiently
    • ✅ Only check active entities
    • ✅ Use simple hitboxes
    • ✅ Limit active collision pairs

    Strings

    • ✅ Use static buffers
    • ✅ Cache text rendering
    • ✅ Avoid string operations in loops

    Common Optimization Patterns

    Update Frequency Reduction

    class LowFrequencyUpdater {
    -private:
    -    unsigned long timer = 0;
    -    unsigned long interval = 100; // Update every 100ms
    -
    -public:
    -    void update(unsigned long deltaTime) {
    -        timer += deltaTime;
    -        if (timer >= interval) {
    -            timer -= interval;
    -            // Do expensive update
    -            expensiveUpdate();
    -        }
    -    }
    -};
    -

    Spatial Partitioning (Simple)

    // Divide screen into zones
    -class SpatialGrid {
    -private:
    -    static const int GRID_SIZE = 4;
    -    static const int CELL_WIDTH = 60;
    -    static const int CELL_HEIGHT = 60;
    -
    -    std::vector<Actor*> grid[GRID_SIZE][GRID_SIZE];
    -
    -public:
    -    void add(Actor* actor) {
    -        int cellX = static_cast<int>(actor->x) / CELL_WIDTH;
    -        int cellY = static_cast<int>(actor->y) / CELL_HEIGHT;
    -        if (cellX >= 0 && cellX < GRID_SIZE && 
    -            cellY >= 0 && cellY < GRID_SIZE) {
    -            grid[cellY][cellX].push_back(actor);
    -        }
    -    }
    -
    -    void checkCollisions() {
    -        // Only check collisions within same cell
    -        for (int y = 0; y < GRID_SIZE; y++) {
    -            for (int x = 0; x < GRID_SIZE; x++) {
    -                auto& cell = grid[y][x];
    -                for (size_t i = 0; i < cell.size(); i++) {
    -                    for (size_t j = i + 1; j < cell.size(); j++) {
    -                        checkCollision(cell[i], cell[j]);
    -                    }
    -                }
    -            }
    -        }
    -    }
    -};
    -

    Troubleshooting

    Low FPS

    • Profile to find bottlenecks
    • Reduce entity count
    • Optimize rendering (culling, batching)
    • Simplify collision detection
    • Reduce update frequency

    Frame Drops

    • Check for expensive operations in update()
    • Avoid dynamic allocation
    • Cache calculations
    • Reduce draw calls

    Stuttering

    • Ensure frame-rate independence (use deltaTime)
    • Avoid blocking operations
    • Pre-load resources
    • Use object pooling

    Next Steps

    Now that you understand performance optimization, learn about: - Memory Management - Manage memory efficiently - Platforms and Drivers - Platform-specific optimizations - Extensibility - Extend the engine


    See also: - Manual - Basic Rendering - Manual - Physics and Collisions

    \ No newline at end of file + void checkCollisions() { + // Only check collisions within same cell + for (int y = 0; y < GRID_SIZE; y++) { + for (int x = 0; x < GRID_SIZE; x++) { + auto& cell = grid[y][x]; + for (size_t i = 0; i < cell.size(); i++) { + for (size_t j = i + 1; j < cell.size(); j++) { + checkCollision(cell[i], cell[j]); + } + } + } + } + } +}; +

    Troubleshooting

    Low FPS

    • Profile to find bottlenecks
    • Reduce entity count
    • Optimize rendering (culling, batching)
    • Simplify collision detection
    • Reduce update frequency

    Frame Drops

    • Check for expensive operations in update()
    • Avoid dynamic allocation
    • Cache calculations
    • Reduce draw calls

    Stuttering

    • Ensure frame-rate independence (use deltaTime)
    • Avoid blocking operations
    • Pre-load resources
    • Use object pooling

    Next Steps

    Now that you understand performance optimization, learn about: - Memory Management - Manage memory efficiently - Platforms and Drivers - Platform-specific optimizations - Extensibility - Extend the engine


    See also: - Manual - Basic Rendering - Manual - Physics and Collisions

    \ No newline at end of file diff --git a/site/manual/optimization/platforms_and_drivers/index.html b/site/manual/optimization/platforms_and_drivers/index.html index 6e59cb8..f2923c4 100644 --- a/site/manual/optimization/platforms_and_drivers/index.html +++ b/site/manual/optimization/platforms_and_drivers/index.html @@ -1,4 +1,4 @@ - Platforms and Drivers - PixelRoot32 Documentation

    Platforms and Drivers

    PixelRoot32 supports multiple platforms through driver abstraction. This guide covers supported platforms, display drivers, audio backends, and build configuration.

    Supported Platforms

    ESP32

    Primary platform for PixelRoot32 games.

    Characteristics: - TFT_eSPI display driver - Internal DAC or I2S audio - GPIO button input - Limited RAM/Flash - Real hardware constraints

    Use for: - Final game deployment - Hardware testing - Production builds

    Native/Desktop (SDL2)

    Development platform for rapid iteration.

    Characteristics: - SDL2 display driver - SDL2 audio backend - Keyboard input - Unlimited resources (for testing) - Fast development cycle

    Use for: - Development and debugging - Testing without hardware - Rapid prototyping - CI/CD testing

    Display Drivers

    TFT_eSPI (ESP32)

    TFT_eSPI is the display driver for ESP32, supporting many TFT displays.

    Configuration

    Configure TFT_eSPI via build flags in platformio.ini:

    [env:esp32dev]
    + Platforms and Drivers - PixelRoot32 Documentation      

    Platforms and Drivers

    PixelRoot32 supports multiple platforms through driver abstraction. This guide covers supported platforms, display drivers, audio backends, and build configuration.

    Supported Platforms

    ESP32

    Primary platform for PixelRoot32 games.

    Characteristics: - TFT_eSPI display driver - Internal DAC or I2S audio - GPIO button input - Limited RAM/Flash - Real hardware constraints

    Use for: - Final game deployment - Hardware testing - Production builds

    Native/Desktop (SDL2)

    Development platform for rapid iteration.

    Characteristics: - SDL2 display driver - SDL2 audio backend - Keyboard input - Unlimited resources (for testing) - Fast development cycle

    Use for: - Development and debugging - Testing without hardware - Rapid prototyping - CI/CD testing

    Display Drivers

    TFT_eSPI (ESP32)

    TFT_eSPI is the display driver for ESP32, supporting many TFT displays.

    Optimizaciones ESP32

    Para maximizar el rendimiento en ESP32, PixelRoot32 utiliza:

    • DMA (Direct Memory Access): Las transferencias al display se realizan en segundo plano, permitiendo que la CPU prepare el siguiente frame mientras se envía el actual.
    • Doble Buffer con IRAM: El motor utiliza un buffer de pantalla (Sprite de TFT_eSPI) optimizado para transferencias rápidas.
    • Alineación de 16 bits: Los datos de sprites 2bpp/4bpp están alineados a palabras de 16 bits para aprovechar la arquitectura Xtensa.
    • IRAM_ATTR: Las funciones críticas de renderizado están marcadas para residir en la RAM de instrucciones, evitando cuellos de botella por acceso a la Flash.

    Configuración DMA

    El DMA se activa automáticamente si el hardware lo soporta. Asegúrate de configurar la frecuencia SPI adecuada para tu display (usualmente 40MHz u 80MHz).

    [env:esp32dev]
     build_flags = 
         -D ST7789_DRIVER          # Display type
         -D TFT_WIDTH=240          # Display width
    @@ -21,7 +21,7 @@
     
     // TFT_eSPI_Drawer is created automatically by Engine
     // No manual driver creation needed
    -

    SDL2_Drawer (Native)

    SDL2_Drawer provides display output for PC/desktop development.

    Configuration

    #include <drivers/native/SDL2_Drawer.h>
    +

    SDL2_Drawer (Native)

    SDL2_Drawer provides display output for PC/desktop development.

    Configuration

    #include <drivers/native/SDL2_Drawer.h>
     
     // Display configuration (NONE defaults to SDL2)
     pixelroot32::graphics::DisplayConfig displayConfig(
    @@ -35,7 +35,7 @@
     

    SDL2 Installation

    Windows (MSYS2):

    pacman -S mingw-w64-x86_64-SDL2
     

    Linux:

    sudo apt-get install libsdl2-dev
     

    macOS:

    brew install sdl2
    -

    Audio Backends

    ESP32_DAC_AudioBackend

    Uses ESP32's internal DAC for audio output.

    Configuration

    #include <drivers/esp32/ESP32_DAC_AudioBackend.h>
    +

    Audio Backends

    ESP32_DAC_AudioBackend

    Uses ESP32's internal DAC for audio output.

    Configuration

    #include <drivers/esp32/ESP32_DAC_AudioBackend.h>
     
     const int DAC_PIN = 25; // GPIO 25 or 26
     pixelroot32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(
    @@ -47,7 +47,7 @@
         &audioBackend, 
         audioBackend.getSampleRate()
     );
    -

    Characteristics: - Simple setup (just one pin) - Lower quality than I2S - Good for basic audio - Sample rate: 11025 Hz recommended

    ESP32_I2S_AudioBackend

    Uses ESP32's I2S peripheral for higher quality audio.

    Configuration

    #include <drivers/esp32/ESP32_I2S_AudioBackend.h>
    +

    Characteristics: - Simple setup (just one pin) - Lower quality than I2S - Good for basic audio - Sample rate: 11025 Hz recommended

    ESP32_I2S_AudioBackend

    Uses ESP32's I2S peripheral for higher quality audio.

    Configuration

    #include <drivers/esp32/ESP32_I2S_AudioBackend.h>
     
     const int I2S_BCLK = 26;  // Bit clock pin
     const int I2S_LRCK = 25;  // Left/Right clock pin
    @@ -61,7 +61,7 @@
     );
     
     pixelroot32::audio::AudioConfig audioConfig(&audioBackend, 22050);
    -

    Characteristics: - Higher quality than DAC - Requires external I2S DAC (e.g., MAX98357A) - Better for music - Sample rate: 22050 Hz recommended

    SDL2_AudioBackend (Native)

    SDL2 audio for PC development.

    Configuration

    #include <drivers/native/SDL2_AudioBackend.h>
    +

    Characteristics: - Higher quality than DAC - Requires external I2S DAC (e.g., MAX98357A) - Better for music - Sample rate: 22050 Hz recommended

    SDL2_AudioBackend (Native)

    SDL2 audio for PC development.

    Configuration

    #include <drivers/native/SDL2_AudioBackend.h>
     
     pixelroot32::drivers::native::SDL2_AudioBackend audioBackend(
         22050,  // Sample rate
    @@ -74,96 +74,99 @@
         -D PIXELROOT32_ENABLE_2BPP_SPRITES    # Enable 2bpp sprite format
         -D PIXELROOT32_ENABLE_4BPP_SPRITES   # Enable 4bpp sprite format
         -D PIXELROOT32_ENABLE_SCENE_ARENA    # Enable Scene Arena (experimental)
    -

    Platform Detection

    #ifdef PLATFORM_ESP32
    -    // ESP32-specific code
    -    Serial.println("Running on ESP32");
    -#endif
    -
    -#ifdef PLATFORM_NATIVE
    -    // Native/PC-specific code
    -    printf("Running on PC\n");
    -#endif
    -

    Optimization Flags

    [env:esp32dev]
    -build_flags = 
    -    -O2              # Optimization level
    -    -ffunction-sections
    -    -fdata-sections
    -    -Wl,--gc-sections
    -

    Complete Platform Setup Examples

    ESP32 Complete Setup

    #include <Arduino.h>
    -#include <core/Engine.h>
    -#include <drivers/esp32/TFT_eSPI_Drawer.h>
    -#include <drivers/esp32/ESP32_DAC_AudioBackend.h>
    -
    -namespace pr32 = pixelroot32;
    -
    -// Audio
    -const int DAC_PIN = 25;
    -pr32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(DAC_PIN, 11025);
    -
    -// Display
    -pr32::graphics::DisplayConfig displayConfig(
    -    pr32::graphics::DisplayType::ST7789,
    -    0, 240, 240
    -);
    -
    -// Input
    -pr32::input::InputConfig inputConfig(
    -    6, 32, 27, 33, 14, 13, 12  // 6 buttons, pins
    -);
    -
    -// Audio config
    -pr32::audio::AudioConfig audioConfig(&audioBackend, 11025);
    -
    -// Engine
    -pr32::core::Engine engine(displayConfig, inputConfig, audioConfig);
    -
    -void setup() {
    -    Serial.begin(115200);
    -    engine.init();
    -    // ... scene setup
    -}
    -
    -void loop() {
    -    engine.run();
    -}
    -

    Native Complete Setup

    #define SDL_MAIN_HANDLED
    -#include <SDL2/SDL.h>
    -#include <core/Engine.h>
    -#include <drivers/native/SDL2_Drawer.h>
    -#include <drivers/native/SDL2_AudioBackend.h>
    -
    -namespace pr32 = pixelroot32;
    -
    -// Audio
    -pr32::drivers::native::SDL2_AudioBackend audioBackend(22050, 1024);
    +

    Scene limits (MAX_LAYERS / MAX_ENTITIES)

    You can override the default scene limits from your project without modifying the engine. The default of 3 for MAX_LAYERS is due to ESP32 platform constraints (memory and draw-loop cost); on native/PC you can use a higher value.

    Option A: Compiler flags (recommended) — in platformio.ini, add to build_flags for your environment:

    build_flags =
    +    -DMAX_LAYERS=5
    +    -DMAX_ENTITIES=64
    +

    The compiler defines these before any .cpp is processed. Because Scene.h uses #ifndef MAX_LAYERS / #ifndef MAX_ENTITIES, your values are used (more render layers drawn in Scene::draw, and on Arduino the entity queue capacity when built with MAX_ENTITIES).

    See API Reference - Scene - Overriding scene limits for details.

    Platform Detection

    #ifdef PLATFORM_ESP32
    +    // ESP32-specific code
    +    Serial.println("Running on ESP32");
    +#endif
    +
    +#ifdef PLATFORM_NATIVE
    +    // Native/PC-specific code
    +    printf("Running on PC\n");
    +#endif
    +

    Optimization Flags

    [env:esp32dev]
    +build_flags = 
    +    -O2              # Optimization level
    +    -ffunction-sections
    +    -fdata-sections
    +    -Wl,--gc-sections
    +

    Complete Platform Setup Examples

    ESP32 Complete Setup

    #include <Arduino.h>
    +#include <core/Engine.h>
    +#include <drivers/esp32/TFT_eSPI_Drawer.h>
    +#include <drivers/esp32/ESP32_DAC_AudioBackend.h>
    +
    +namespace pr32 = pixelroot32;
    +
    +// Audio
    +const int DAC_PIN = 25;
    +pr32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(DAC_PIN, 11025);
     
     // Display
     pr32::graphics::DisplayConfig displayConfig(
    -    pr32::graphics::DisplayType::NONE,
    +    pr32::graphics::DisplayType::ST7789,
         0, 240, 240
     );
     
    -// Input (SDL scancodes)
    +// Input
     pr32::input::InputConfig inputConfig(
    -    6,
    -    SDL_SCANCODE_UP,
    -    SDL_SCANCODE_DOWN,
    -    SDL_SCANCODE_LEFT,
    -    SDL_SCANCODE_RIGHT,
    -    SDL_SCANCODE_SPACE,
    -    SDL_SCANCODE_RETURN
    -);
    +    6, 32, 27, 33, 14, 13, 12  // 6 buttons, pins
    +);
    +
    +// Audio config
    +pr32::audio::AudioConfig audioConfig(&audioBackend, 11025);
    +
    +// Engine
    +pr32::core::Engine engine(displayConfig, inputConfig, audioConfig);
     
    -// Audio config
    -pr32::audio::AudioConfig audioConfig(&audioBackend, 22050);
    -
    -// Engine
    -pr32::core::Engine engine(displayConfig, inputConfig, audioConfig);
    +void setup() {
    +    Serial.begin(115200);
    +    engine.init();
    +    // ... scene setup
    +}
     
    -int main(int argc, char* argv[]) {
    -    engine.init();
    -    // ... scene setup
    -    engine.run();
    -    return 0;
    -}
    -

    Platform-Specific Considerations

    ESP32

    Memory: - Limited RAM (~320KB) - Use object pooling - Store data in flash - Avoid dynamic allocation

    Performance: - Target 30-60 FPS - Optimize rendering - Reduce entity count - Profile on hardware

    Hardware: - GPIO pin configuration - SPI display setup - Audio hardware connections - Power considerations

    Native

    Development: - Fast iteration - Easy debugging - Unlimited resources - Visual debugging tools

    Testing: - Test logic without hardware - Rapid prototyping - CI/CD integration - Cross-platform testing

    Troubleshooting

    ESP32 Display Issues

    • Check wiring connections
    • Verify pin numbers
    • Lower SPI frequency
    • Check display type matches
    • Verify power supply

    ESP32 Audio Issues

    • Check DAC/I2S pin configuration
    • Verify sample rate
    • Check hardware connections
    • Lower volume if distorted
    • Test with different sample rates

    Native Build Issues

    • Verify SDL2 installation
    • Check include/library paths
    • Ensure SDL2 version compatibility
    • Check linker flags

    Next Steps

    Now that you understand platforms and drivers, learn about: - Extensibility - Create custom drivers - Memory Management - ESP32 memory constraints - Performance Optimization - Platform-specific optimization


    See also: - API Reference - DrawSurface - API Reference - AudioBackend - Getting Started - Your First Project

    \ No newline at end of file +void loop() { + engine.run(); +} +

    Native Complete Setup

    #define SDL_MAIN_HANDLED
    +#include <SDL2/SDL.h>
    +#include <core/Engine.h>
    +#include <drivers/native/SDL2_Drawer.h>
    +#include <drivers/native/SDL2_AudioBackend.h>
    +
    +namespace pr32 = pixelroot32;
    +
    +// Audio
    +pr32::drivers::native::SDL2_AudioBackend audioBackend(22050, 1024);
    +
    +// Display
    +pr32::graphics::DisplayConfig displayConfig(
    +    pr32::graphics::DisplayType::NONE,
    +    0, 240, 240
    +);
    +
    +// Input (SDL scancodes)
    +pr32::input::InputConfig inputConfig(
    +    6,
    +    SDL_SCANCODE_UP,
    +    SDL_SCANCODE_DOWN,
    +    SDL_SCANCODE_LEFT,
    +    SDL_SCANCODE_RIGHT,
    +    SDL_SCANCODE_SPACE,
    +    SDL_SCANCODE_RETURN
    +);
    +
    +// Audio config
    +pr32::audio::AudioConfig audioConfig(&audioBackend, 22050);
    +
    +// Engine
    +pr32::core::Engine engine(displayConfig, inputConfig, audioConfig);
    +
    +int main(int argc, char* argv[]) {
    +    engine.init();
    +    // ... scene setup
    +    engine.run();
    +    return 0;
    +}
    +

    Platform-Specific Considerations

    ESP32

    Memory: - Limited RAM (~320KB) - Use object pooling - Store data in flash - Avoid dynamic allocation

    Performance: - Target 30-60 FPS - Optimize rendering - Reduce entity count - Profile on hardware

    Hardware: - GPIO pin configuration - SPI display setup - Audio hardware connections - Power considerations

    Native

    Development: - Fast iteration - Easy debugging - Unlimited resources - Visual debugging tools

    Testing: - Test logic without hardware - Rapid prototyping - CI/CD integration - Cross-platform testing

    Troubleshooting

    ESP32 Display Issues

    • Check wiring connections
    • Verify pin numbers
    • Lower SPI frequency
    • Check display type matches
    • Verify power supply

    ESP32 Audio Issues

    • Check DAC/I2S pin configuration
    • Verify sample rate
    • Check hardware connections
    • Lower volume if distorted
    • Test with different sample rates

    Native Build Issues

    • Verify SDL2 installation
    • Check include/library paths
    • Ensure SDL2 version compatibility
    • Check linker flags

    Next Steps

    Now that you understand platforms and drivers, learn about: - Extensibility - Create custom drivers - Memory Management - ESP32 memory constraints - Performance Optimization - Platform-specific optimization


    See also: - API Reference - DrawSurface - API Reference - AudioBackend - Getting Started - Your First Project

    \ No newline at end of file diff --git a/site/reference/api_overview/index.html b/site/reference/api_overview/index.html index b09da99..033fa57 100644 --- a/site/reference/api_overview/index.html +++ b/site/reference/api_overview/index.html @@ -1,7 +1,7 @@ - API Overview - PixelRoot32 Documentation

    API Reference Overview

    This document provides a complete technical reference for all PixelRoot32 APIs, organized by module. Each class includes descriptions, constructors, methods, properties, and usage examples.

    Organization

    The API is organized into the following modules:

    • Core: Engine, Scene, Entity, Actor, PhysicsActor, SceneManager
    • Graphics: Renderer, Camera2D, Color, Font, Sprite, TileMap, DrawSurface
    • Audio: AudioEngine, MusicPlayer, AudioTypes, AudioConfig, AudioBackend
    • Input: InputManager, InputConfig
    • Physics: CollisionSystem, CollisionTypes
    • UI: UIElement, UIButton, UILabel, UILayouts
    • Particles: ParticleEmitter, ParticleConfig, ParticlePresets

    Quick Navigation

    Core Module

    Graphics Module

    Audio Module

    Physics Module

    UI Module

    API Documentation Format

    Each API reference page follows this structure:

    Class Name

    Brief description of the class and its purpose.

    Namespace

    namespace pixelroot32::module {
    + API Overview - PixelRoot32 Documentation      

    API Reference Overview

    This document provides a complete technical reference for all PixelRoot32 APIs, organized by module. Each class includes descriptions, constructors, methods, properties, and usage examples.

    Organization

    The API is organized into the following modules:

    • Core: Engine, Scene, Entity, Actor, PhysicsActor, SceneManager
    • Graphics: Renderer, Camera2D, Color, Font, Sprite, TileMap, DrawSurface
    • Audio: AudioEngine, MusicPlayer, AudioTypes, AudioConfig, AudioBackend
    • Input: InputManager, InputConfig
    • Physics: CollisionSystem, CollisionTypes
    • UI: UIElement, UIButton, UILabel, UILayouts
    • Particles: ParticleEmitter, ParticleConfig, ParticlePresets

    Quick Navigation

    Core Module

    Graphics Module

    Audio Module

    Physics Module

    UI Module

    API Documentation Format

    Each API reference page follows this structure:

    Class Name

    Brief description of the class and its purpose.

    Namespace

    namespace pixelroot32::module {
         class ClassName {
             // ...
         };
     }
     

    Constructors

    List of all constructors with parameters.

    Public Methods

    Method Description Parameters Returns
    methodName() Description param: type return type

    Properties

    Property Type Description
    property type Description

    Usage Example

    // Example code showing typical usage
    -

    Performance Notes

    Any performance considerations or limitations.

    See Also

    Links to related APIs and documentation.

    Finding APIs

    By Functionality

    By Module

    Navigate to the specific module folder: - api_reference/core/ - Core engine classes - api_reference/graphics/ - Rendering and graphics - api_reference/audio/ - Audio system - api_reference/physics/ - Physics and collisions - api_reference/ui/ - User interface

    Complete API List

    Core

    Graphics

    Audio

    Physics

    UI


    Note: This is an overview. For detailed API documentation, see the individual reference pages linked above.

    \ No newline at end of file +

    Performance Notes

    Any performance considerations or limitations.

    See Also

    Links to related APIs and documentation.

    Finding APIs

    By Functionality

    By Module

    Navigate to the specific module folder: - api_reference/core/ - Core engine classes - api_reference/graphics/ - Rendering and graphics - api_reference/audio/ - Audio system - api_reference/physics/ - Physics and collisions - api_reference/ui/ - User interface

    Complete API List

    Core

    Graphics

    Audio

    Physics

    UI


    Note: This is an overview. For detailed API documentation, see the individual reference pages linked above.

    \ No newline at end of file diff --git a/site/reference/code_examples/index.html b/site/reference/code_examples/index.html index 18576d4..53d062e 100644 --- a/site/reference/code_examples/index.html +++ b/site/reference/code_examples/index.html @@ -1,4 +1,4 @@ - Code Examples - PixelRoot32 Documentation

    Code Examples

    A library of reusable code snippets for common PixelRoot32 tasks. All examples are complete and functional.

    Initialization

    Basic Engine Setup (ESP32)

    #include <Arduino.h>
    + Code Examples - PixelRoot32 Documentation      

    Code Examples

    A library of reusable code snippets for common PixelRoot32 tasks. All examples are complete and functional.

    Initialization

    Basic Engine Setup (ESP32)

    #include <Arduino.h>
     #include <core/Engine.h>
     #include <drivers/esp32/TFT_eSPI_Drawer.h>
     #include <drivers/esp32/ESP32_DAC_AudioBackend.h>
    @@ -605,4 +605,4 @@
         bool isActive() const { return active; }
         float getProgress() const { return static_cast<float>(elapsed) / duration; }
     };
    -

    See Also

    \ No newline at end of file +

    See Also

    \ No newline at end of file diff --git a/site/reference/game_examples_guide/index.html b/site/reference/game_examples_guide/index.html index 67c63f4..b29ca4e 100644 --- a/site/reference/game_examples_guide/index.html +++ b/site/reference/game_examples_guide/index.html @@ -1,4 +1,4 @@ - Game Examples Guide - PixelRoot32 Documentation

    Game Examples Guide

    This guide analyzes the complete game examples included with PixelRoot32, explaining their architecture, patterns, and lessons learned.

    Available Examples

    PixelRoot32 includes several complete game examples:

    • Space Invaders: Full-featured shooter with enemies, projectiles, and audio
    • Pong: Classic arcade game with physics and collisions
    • Snake: Grid-based game with entity pooling
    • TicTacToe: Turn-based game with simple AI
    • CameraDemo: Platformer with scrolling camera and parallax
    • SpritesDemo: Demonstration of advanced sprite formats

    Space Invaders

    Location: src/examples/SpaceInvaders/

    Architecture

    Space Invaders demonstrates a complete game with multiple systems:

    • Scene Management: SpaceInvadersScene manages game state
    • Actor Hierarchy: PlayerActor, AlienActor, ProjectileActor, BunkerActor
    • Collision System: Uses collision layers for player, enemies, projectiles
    • Audio Integration: Sound effects for shooting, explosions, music
    • Tilemap Background: Starfield using tilemap system

    Key Systems

    Collision Layers

    namespace Layers {
    + Game Examples Guide - PixelRoot32 Documentation      

    Game Examples Guide

    This guide analyzes the complete game examples included with PixelRoot32, explaining their architecture, patterns, and lessons learned.

    Available Examples

    PixelRoot32 (en el proyecto PixelRoot32 Game Samples) incluye estos juegos y demos:

    • Metroidvania: Plataformas 2D con tilemap 4bpp multicapa y colisión tile-based (requiere PIXELROOT32_ENABLE_4BPP_SPRITES; sin scroll/cámara)
    • Space Invaders: Shooter completo con enemigos, proyectiles, búnkeres y audio
    • Pong: Clásico con física y colisiones
    • BrickBreaker: Estilo Breakout con partículas y audio avanzado
    • Snake: Juego en rejilla con entity pooling
    • TicTacToe: Turnos y IA simple
    • CameraDemo: Plataformas con cámara y parallax
    • SpritesDemo: Sprites 2bpp y 4bpp
    • TileMapDemo: Tilemaps 4bpp (con viewport culling)

    Space Invaders

    Location: src/examples/SpaceInvaders/

    Architecture

    Space Invaders demonstrates a complete game with multiple systems:

    • Scene Management: SpaceInvadersScene manages game state
    • Actor Hierarchy: PlayerActor, AlienActor, ProjectileActor, BunkerActor
    • Collision System: Uses collision layers for player, enemies, projectiles
    • Audio Integration: Sound effects for shooting, explosions, music
    • Background: Starfield (patrón de estrellas en código) o tilemap

    Key Systems

    Collision Layers

    namespace Layers {
         constexpr uint16_t PLAYER = 0x0001;
         constexpr uint16_t ALIEN = 0x0002;
         constexpr uint16_t PROJECTILE = 0x0004;
    @@ -12,7 +12,7 @@
     // Projectiles can hit aliens and bunkers
     projectile->setCollisionLayer(Layers::PROJECTILE);
     projectile->setCollisionMask(Layers::ALIEN | Layers::BUNKER);
    -

    Entity Management

    • Uses object pooling for projectiles
    • Manages alien formation with grid layout
    • Handles game state (playing, game over)

    Audio Integration

    • Background music using MusicPlayer
    • Sound effects for various events
    • Audio events triggered on collisions

    Patterns Used

    • Object Pooling: Projectiles are pooled and reused
    • State Machine: Game states (playing, game over, victory)
    • Grid Layout: Alien formation uses grid-based positioning
    • Event-Driven Audio: Sounds triggered by game events

    Lessons Learned

    • Collision layers are essential for complex games
    • Object pooling improves performance
    • Tilemaps are efficient for backgrounds
    • Audio enhances game feel significantly

    Pong

    Location: src/examples/Pong/

    Architecture

    Pong demonstrates physics and collision handling:

    • PhysicsActor: Ball uses PhysicsActor for automatic physics
    • Collision Callbacks: Paddles and ball handle collisions
    • Score System: Simple score tracking and display
    • Game State: Reset and game over handling

    Key Systems

    Physics Setup

    class BallActor : public pixelroot32::core::PhysicsActor {
    +

    Entity Management

    • Uses object pooling for projectiles
    • Manages alien formation with grid layout
    • Handles game state (playing, game over)

    Audio Integration

    • Background music using MusicPlayer
    • Sound effects for various events
    • Audio events triggered on collisions

    Patterns Used

    • Object Pooling: Projectiles are pooled and reused
    • State Machine: Game states (playing, game over, victory)
    • Grid Layout: Alien formation uses grid-based positioning
    • Event-Driven Audio: Sounds triggered by game events

    Lessons Learned

    • Collision layers are essential for complex games
    • Object pooling improves performance
    • Starfield or tilemap backgrounds are efficient
    • Audio enhances game feel significantly

    Metroidvania

    Ubicación: src/examples/Games/Metroidvania/

    Arquitectura

    Metroidvania es un ejemplo de plataformas 2D con tilemap multicapa y optimizaciones pensadas para ESP32. No usa scroll ni cámara; el nivel se dibuja con origen fijo (0,0).

    • Scene: MetroidvaniaScene con un único PlayerActor y varias capas de tilemap (background, platforms, details, stairs).
    • PlayerActor: Movimiento horizontal y vertical, escaleras, colisión tile-based (sin listas de rectángulos).
    • Tilemap: 4bpp (TileMap4bpp), con viewport culling y caché de paleta en el motor. Nivel 40×30 tiles (320×240 px).
    • Sin cámara: La vista no sigue al jugador; para scroll habría que usar Camera2D y aplicar offset en el renderer (como en CameraDemo).

    Características del motor usadas

    • Colisión tile-based: Comprobación directa de tiles alrededor del jugador (getTileAt), en lugar de iterar sobre platformRects.
    • Sprites 4bpp: Player con animaciones (idle, run, jump) desde headers generados (p. ej. Sprite Compiler).
    • Optimizaciones de renderizado: Viewport culling en drawTileMap, drawSprite 4bpp optimizado, capas culleadas por viewport.
    • Opcional: Scene arena, DMA, IRAM_ATTR en rutas críticas (según plan de optimización del ejemplo).

    Patrones utilizados

    • Tile-based collision: Un solo acceso por tile en O(1) en lugar de O(N) rectángulos.
    • Detección de escaleras: Un solo resultado reutilizado para colisión y cambio de estado.
    • Hitbox simplificada: Menos puntos de comprobación vertical (cabeza y pies).

    Lecciones

    • La colisión tile-based escala mejor que listas de rectángulos en niveles grandes.
    • Las optimizaciones de viewport y 4bpp mejoran FPS en ESP32.
    • Metroidvania sirve de referencia para juegos de plataformas con tilemap y cámara.

    Pong

    Location: src/examples/Pong/

    Architecture

    Pong demonstrates physics and collision handling:

    • PhysicsActor: Ball uses PhysicsActor for automatic physics
    • Collision Callbacks: Paddles and ball handle collisions
    • Score System: Simple score tracking and display
    • Game State: Reset and game over handling

    Key Systems

    Physics Setup

    class BallActor : public pixelroot32::core::PhysicsActor {
     public:
         BallActor(float x, float y, float speed, int radius)
             : PhysicsActor(x, y, radius * 2, radius * 2) {
    @@ -171,4 +171,4 @@
         // 4. Draw UI/HUD
         drawHUD(renderer);
     }
    -

    Learning Path

    Beginner Examples

    1. Pong: Start here - simple physics and collisions
    2. TicTacToe: Learn turn-based logic
    3. Snake: Understand entity pooling

    Intermediate Examples

    1. CameraDemo: Learn scrolling and parallax
    2. SpritesDemo: Explore sprite formats

    Advanced Examples

    1. Space Invaders: Complete game with all systems

    Code Study Recommendations

    For Learning Physics

    • Study Pong/BallActor.cpp - PhysicsActor usage
    • Study CameraDemo/PlayerCube.cpp - Platformer physics

    For Learning Collisions

    • Study SpaceInvaders - Complex collision layers
    • Study Pong - Simple collision response

    For Learning Memory Management

    • Study Snake/SnakeScene.cpp - Entity pooling
    • Study SpaceInvaders - Projectile pooling

    For Learning Audio

    • Study SpaceInvaders - Music and sound effects
    • Study Pong - Simple audio integration

    For Learning UI

    • Study TicTacToe - Button-based UI
    • Study menu scenes - Layout usage

    Extending Examples

    Adding Features

    • Pong: Add power-ups, multiple balls
    • Snake: Add obstacles, multiple food types
    • Space Invaders: Add boss battles, power-ups

    Creating Variations

    • Pong: Make it vertical, add walls
    • Snake: Change to hexagonal grid
    • TicTacToe: Make it 4x4 or 5x5

    Best Practices from Examples

    1. Pre-allocate Resources: All examples pre-allocate entities
    2. Use Object Pooling: For frequently created/destroyed entities
    3. Organize by Layers: Clear collision layer organization
    4. Separate Concerns: Game logic separate from rendering
    5. State Management: Clear game state handling

    See Also


    Note: All example code is available in the src/examples/ directory of the PixelRoot32 Game Samples project.

    \ No newline at end of file +

    Learning Path

    Beginner Examples

    1. Pong: Física y colisiones básicas
    2. TicTacToe: Lógica por turnos
    3. Snake: Entity pooling y rejilla

    Intermediate Examples

    1. CameraDemo: Cámara y parallax
    2. SpritesDemo: Formatos 2bpp y 4bpp
    3. BrickBreaker: Física, partículas y audio

    Advanced Examples

    1. Space Invaders: Juego completo (sprites 1bpp, colisiones, audio)
    2. Metroidvania: Plataformas con tilemap 4bpp multicapa, colisión tile-based y optimizaciones ESP32 (sin scroll/cámara)

    Code Study Recommendations

    For Learning Physics

    • Study Pong/BallActor.cpp - PhysicsActor usage
    • Study CameraDemo/PlayerCube.cpp - Platformer physics

    For Learning Collisions

    • Study SpaceInvaders - Complex collision layers
    • Study Pong - Simple collision response

    For Learning Memory Management

    • Study Snake/SnakeScene.cpp - Entity pooling
    • Study SpaceInvaders - Projectile pooling

    For Learning Audio

    • Study SpaceInvaders - Music and sound effects
    • Study Pong - Simple audio integration

    For Learning UI

    • Study TicTacToe - Button-based UI
    • Study menu scenes - Layout usage

    Extending Examples

    Adding Features

    • Pong: Add power-ups, multiple balls
    • Snake: Add obstacles, multiple food types
    • Space Invaders: Add boss battles, power-ups

    Creating Variations

    • Pong: Make it vertical, add walls
    • Snake: Change to hexagonal grid
    • TicTacToe: Make it 4x4 or 5x5

    Best Practices from Examples

    1. Pre-allocate Resources: All examples pre-allocate entities
    2. Use Object Pooling: For frequently created/destroyed entities
    3. Organize by Layers: Clear collision layer organization
    4. Separate Concerns: Game logic separate from rendering
    5. State Management: Clear game state handling

    See Also


    Note: All example code is available in the src/examples/ directory of the PixelRoot32 Game Samples project.

    \ No newline at end of file diff --git a/site/resources/available_tools/index.html b/site/resources/available_tools/index.html index b65470b..2a22eef 100644 --- a/site/resources/available_tools/index.html +++ b/site/resources/available_tools/index.html @@ -1,15 +1,34 @@ - Available Tools - PixelRoot32 Documentation

    Available Tools

    This guide documents tools available to facilitate PixelRoot32 game development.

    Sprite Compiler (pr32-sprite-compiler)

    The Sprite Compiler converts PNG images to PixelRoot32 sprite data formats, making it easy to create sprites from image files.

    Read more in the Sprite Compiler Guide

    Tilemap Editor

    The Tilemap Editor is a visual tool for designing multi-layered tile-based environments. It allows you to import tilesets, paint maps, and export optimized C++ code directly for use with PixelRoot32.

    Read more in the Tilemap Editor Guide

    Key Features

    • Multi-layer editing: Up to 8 layers with transparency support.
    • BPP support: Export maps in 1bpp, 2bpp, or 4bpp formats.
    • Visual Tools: Brush, Eraser, Rectangle Fill, and Pipette.
    • Standalone App: Available as a portable .exe for Windows.

    Installation (Quick Start)

    From Source:

    git clone https://github.com/Gperez88/PixelRoot32-Tilemap-Editor.git
    -cd PixelRoot32-Tilemap-Editor
    -pip install ttkbootstrap pillow jinja2
    -python main.py
    -

    From Source:

    git clone https://github.com/Gperez88/pr32-sprite-compiler.git
    + Available Tools - PixelRoot32 Documentation      

    Available Tools

    This guide documents tools available to facilitate PixelRoot32 game development.

    Sprite Compiler (pr32-sprite-compiler)

    The Sprite Compiler converts PNG images to PixelRoot32 sprite data formats, making it easy to create sprites from image files.

    Read more in the Sprite Compiler Guide

    From Source:

    git clone https://github.com/Gperez88/pr32-sprite-compiler.git
     cd pr32-sprite-compiler
     npm install
     npm link  # Optional: install globally
    -

    As NPM Package:

    npm install -g pr32-sprite-compiler
    -

    Basic Usage

    Command Line:

    pr32-sprite-compiler input.png output.h
    -

    With Options:

    pr32-sprite-compiler input.png output.h --format 1bpp --name MY_SPRITE
    -

    Supported Formats

    • 1bpp (default): Monochrome, most memory-efficient
    • 2bpp: 4 colors per sprite (requires PIXELROOT32_ENABLE_2BPP_SPRITES)
    • 4bpp: 16 colors per sprite (requires PIXELROOT32_ENABLE_4BPP_SPRITES)

    Output Format

    The compiler generates C++ header files with sprite data:

    // output.h
    +

    As NPM Package:

    npm install -g pr32-sprite-compiler
    +

    Basic Usage

    Command Line:

    pr32-sprite-compiler input.png output.h
    +

    With Options:

    pr32-sprite-compiler input.png output.h --format 1bpp --name MY_SPRITE
    +

    Supported Formats

    • 1bpp (default): Monochrome, most memory-efficient
    • 2bpp: 4 colors per sprite (requires PIXELROOT32_ENABLE_2BPP_SPRITES)
    • 4bpp: 16 colors per sprite (requires PIXELROOT32_ENABLE_4BPP_SPRITES)

    Output Format

    The compiler generates C++ header files with sprite data:

    // output.h
     #ifndef SPRITE_DATA_H
     #define SPRITE_DATA_H
     
    @@ -29,17 +48,17 @@
     };
     
     #endif
    -

    Advanced Options

    Batch Processing:

    pr32-sprite-compiler --batch sprites/*.png --output-dir sprites/out/
    -

    Custom Palette:

    pr32-sprite-compiler input.png output.h --palette custom_palette.json
    -

    Sprite Sheet:

    pr32-sprite-compiler sheet.png output.h --sheet 8x8 --count 16
    -

    GUI Version

    If available, a GUI version provides visual feedback:

    • Drag and drop images
    • Preview sprite data
    • Adjust settings visually
    • Export to header files

    Step-by-Step Example

    1. Create or find a PNG image (8x8, 16x16, etc.)

    2. Run the compiler:

      pr32-sprite-compiler player.png player_sprite.h --name PLAYER_SPRITE
      -

    3. Include in your project:

      #include "player_sprite.h"
      +

      Advanced Options

      Batch Processing:

      pr32-sprite-compiler --batch sprites/*.png --output-dir sprites/out/
      +

      Custom Palette:

      pr32-sprite-compiler input.png output.h --palette custom_palette.json
      +

      Sprite Sheet:

      pr32-sprite-compiler sheet.png output.h --sheet 8x8 --count 16
      +

      GUI Version

      If available, a GUI version provides visual feedback:

      • Drag and drop images
      • Preview sprite data
      • Adjust settings visually
      • Export to header files

      Step-by-Step Example

      1. Create or find a PNG image (8x8, 16x16, etc.)

      2. Run the compiler:

      pr32-sprite-compiler player.png player_sprite.h --name PLAYER_SPRITE
      +
      1. Include in your project:
      #include "player_sprite.h"
       
       void draw() {
           renderer.drawSprite(PLAYER_SPRITE, 100, 100, Color::White);
       }
      -

    Troubleshooting

    Image too large: - Sprites must be ≤ 16 pixels wide for 1bpp - Reduce image size or split into multiple sprites

    Colors not converting correctly: - Ensure image uses indexed colors - Use black/white for 1bpp - Use 4 colors for 2bpp, 16 for 4bpp

    Output file not found: - Check write permissions - Verify output path exists

    Future Tools

    Music Compiler (Planned)

    A tool to convert music files or MIDI to PixelRoot32 MusicTrack format.

    Planned Features: - MIDI to MusicTrack conversion - Visual music editor - Instrument preset management - Export to C++ header files

    Tilemap Compiler (Planned)

    A tool to create tilemaps from image files or tile editors.

    Planned Features: - Image to tilemap conversion - Tile editor integration - Export to C++ arrays - Collision data generation

    Other Planned Tools

    • Save System Generator: Generate save/load code
    • Asset Packer: Bundle assets for distribution
    • Performance Profiler: Analyze game performance

    Using Tools in Development

    Workflow Integration

    Typical Workflow: 1. Create/edit sprites in image editor 2. Compile sprites to C++ headers 3. Include headers in project 4. Use sprites in code

    Automation:

    # Build script example
    +

    Troubleshooting

    Image too large:

    • Sprites must be ≤ 16 pixels wide for 1bpp
    • Reduce image size or split into multiple sprites

    Colors not converting correctly:

    • Ensure image uses indexed colors
    • Use black/white for 1bpp
    • Use 4 colors for 2bpp, 16 for 4bpp

    Output file not found:

    • Check write permissions
    • Verify output path exists

    Future Tools

    Music Compiler (Planned)

    A tool to convert music files or MIDI to PixelRoot32 MusicTrack format.

    Planned Features:

    • MIDI to MusicTrack conversion
    • Visual music editor
    • Instrument preset management
    • Export to C++ header files

    Tilemap Compiler (Planned)

    A tool to create tilemaps from image files or tile editors.

    Planned Features:

    • Image to tilemap conversion
    • Tile editor integration
    • Export to C++ arrays
    • Collision data generation

    Other Planned Tools

    • Save System Generator: Generate save/load code
    • Asset Packer: Bundle assets for distribution
    • Performance Profiler: Analyze game performance

    Using Tools in Development

    Workflow Integration

    Typical Workflow:

    1. Create/edit sprites in image editor
    2. Compile sprites to C++ headers
    3. Include headers in project
    4. Use sprites in code

    Automation:

    # Build script example
     #!/bin/bash
     pr32-sprite-compiler assets/sprites/*.png --output-dir src/sprites/
     # Continue with build...
    -

    Best Practices

    • Organize assets: Keep source images separate from generated code
    • Version control: Commit generated headers, not source images (or both)
    • Naming conventions: Use consistent naming for sprites
    • Batch processing: Process multiple sprites at once when possible

    See Also


    Note: Tool availability may vary. Check the PixelRoot32 repository for the latest tool information.

    \ No newline at end of file +

    Best Practices

    • Organize assets: Keep source images separate from generated code
    • Version control: Commit generated headers, not source images (or both)
    • Naming conventions: Use consistent naming for sprites
    • Batch processing: Process multiple sprites at once when possible

    See Also


    Note: Tool availability may vary. Check the PixelRoot32 repository for the latest tool information.

    \ No newline at end of file diff --git a/site/resources/faq/index.html b/site/resources/faq/index.html index 4736af6..c35c2a6 100644 --- a/site/resources/faq/index.html +++ b/site/resources/faq/index.html @@ -1,4 +1,4 @@ - FAQ - PixelRoot32 Documentation

    Frequently Asked Questions

    Common questions about PixelRoot32, organized by category.

    General

    What is PixelRoot32?

    PixelRoot32 is a lightweight, modular 2D game engine designed for ESP32 microcontrollers. It provides a complete game development framework with rendering, audio, physics, input, and UI systems, optimized for limited hardware resources.

    What platforms does it support?

    • ESP32: Primary platform (TFT displays, GPIO buttons, DAC/I2S audio)
    • Native/Desktop: Development platform (SDL2, keyboard, SDL2 audio)

    What kind of games can I make?

    PixelRoot32 is ideal for: - Retro/arcade-style games - 2D platformers - Shooters - Puzzle games - Simple RPGs - Educational games

    See Limitations and Considerations for what's not suitable.

    Is it free to use?

    Yes, PixelRoot32 is open source and licensed under the MIT License. You can use it freely for personal and commercial projects.

    Where can I find the source code?

    Installation and Configuration

    How do I install PixelRoot32?

    See Your First Project for detailed installation instructions.

    Quick answer: 1. Install PlatformIO in VS Code 2. Create new ESP32 project 3. Add library dependency: gperez88/PixelRoot32-Game-Engine@0.2.0-dev 4. Configure hardware in platformio.ini

    What version should I use?

    Use the exact version 0.2.0-dev. Do NOT use ^ or fuzzy versioning, as the API may change.

    How do I configure my display?

    Configure TFT_eSPI via build flags in platformio.ini. See Your First Project for examples.

    How do I set up audio?

    Choose an audio backend: - ESP32_DAC: Simple, one pin (GPIO 25 or 26) - ESP32_I2S: Higher quality, requires external DAC - SDL2_AudioBackend: For Native/PC development

    See Audio for details.

    Development

    How do I create a scene?

    Inherit from pixelroot32::core::Scene and implement init(), update(), and draw(). See Scenes and Entities.

    How do I add entities to a scene?

    Create entities and call addEntity() in init(). The scene manages them automatically.

    What's the difference between Entity, Actor, and PhysicsActor?

    • Entity: Base class, can be drawn and updated
    • Actor: Entity with collision detection
    • PhysicsActor: Actor with automatic physics (velocity, gravity, friction)

    See Scenes and Entities for details.

    How do I handle input?

    Access InputManager through the engine:

    auto& input = engine.getInputManager();
    + FAQ - PixelRoot32 Documentation      

    Frequently Asked Questions

    Common questions about PixelRoot32, organized by category.

    General

    What is PixelRoot32?

    PixelRoot32 is a lightweight, modular 2D game engine designed for ESP32 microcontrollers. It provides a complete game development framework with rendering, audio, physics, input, and UI systems, optimized for limited hardware resources.

    What platforms does it support?

    • ESP32: Primary platform (TFT displays, GPIO buttons, DAC/I2S audio)
    • Native/Desktop: Development platform (SDL2, keyboard, SDL2 audio)

    What kind of games can I make?

    PixelRoot32 is ideal for: - Retro/arcade-style games - 2D platformers - Shooters - Puzzle games - Simple RPGs - Educational games

    See Limitations and Considerations for what's not suitable.

    Is it free to use?

    Yes, PixelRoot32 is open source and licensed under the MIT License. You can use it freely for personal and commercial projects.

    Where can I find the source code?

    Installation and Configuration

    How do I install PixelRoot32?

    See Your First Project for detailed installation instructions.

    Quick answer: 1. Install PlatformIO in VS Code 2. Create new ESP32 project 3. Add library dependency: gperez88/PixelRoot32-Game-Engine@0.2.0-dev 4. Configure hardware in platformio.ini

    What version should I use?

    Use the exact version 0.2.0-dev. Do NOT use ^ or fuzzy versioning, as the API may change.

    How do I configure my display?

    Configure TFT_eSPI via build flags in platformio.ini. See Your First Project for examples.

    How do I set up audio?

    Choose an audio backend: - ESP32_DAC: Simple, one pin (GPIO 25 or 26) - ESP32_I2S: Higher quality, requires external DAC - SDL2_AudioBackend: For Native/PC development

    See Audio for details.

    Development

    How do I create a scene?

    Inherit from pixelroot32::core::Scene and implement init(), update(), and draw(). See Scenes and Entities.

    How do I add entities to a scene?

    Create entities and call addEntity() in init(). The scene manages them automatically.

    What's the difference between Entity, Actor, and PhysicsActor?

    • Entity: Base class, can be drawn and updated
    • Actor: Entity with collision detection
    • PhysicsActor: Actor with automatic physics (velocity, gravity, friction)

    See Scenes and Entities for details.

    How do I handle input?

    Access InputManager through the engine:

    auto& input = engine.getInputManager();
     if (input.isButtonPressed(Buttons::A)) {
         // Handle input
     }
    @@ -11,4 +11,4 @@
     Serial.print("Free heap: ");
     Serial.println(ESP.getFreeHeap());
     #endif
    -

    Hardware

    Which ESP32 board should I use?

    Any ESP32 board works. Common choices: - ESP32-WROOM-32 - ESP32-WROVER (more RAM) - ESP32-DevKit

    Which display should I use?

    Popular choices: - ST7789: 240x240, good quality - ST7735: 128x128, smaller/cheaper - ILI9341: 240x320, larger

    See Platforms and Drivers.

    How many buttons do I need?

    Minimum: 4 (UP, DOWN, LEFT, RIGHT) Recommended: 6 (add A and B buttons) More buttons can be added if needed.

    Can I use analog joysticks?

    Not directly supported. You can read analog pins manually and convert to digital input, but the engine expects digital buttons.

    Troubleshooting

    My display is blank. What's wrong?

    1. Check wiring connections
    2. Verify pin numbers in platformio.ini
    3. Lower SPI frequency
    4. Check display type matches hardware
    5. Verify power supply

    See Troubleshooting.

    Buttons don't work. Why?

    1. Check button wiring
    2. Verify pin numbers in InputConfig
    3. Check pull-up/pull-down resistors
    4. Ensure InputManager is being updated
    5. Test with isButtonDown() vs isButtonPressed()

    Audio is distorted. How do I fix it?

    1. Lower volume levels
    2. Reduce sample rate (try 11025 Hz)
    3. Check for too many simultaneous sounds
    4. Verify hardware connections
    5. Check power supply

    Game crashes randomly. What's happening?

    Common causes: - Out of memory - Too many entities - Infinite loops - Stack overflow - Watchdog timeout

    See Troubleshooting for debugging techniques.

    Advanced

    Can I extend the engine?

    Yes, PixelRoot32 is designed to be extensible. You can: - Create custom display drivers - Create custom audio backends - Extend existing systems

    See Extensibility.

    Can I use 3D graphics?

    No, PixelRoot32 is 2D-only. It's designed for sprite-based 2D games.

    Can I add networking?

    Not currently supported. The engine focuses on single-player games.

    Can I save game data?

    Not currently supported. A save system is planned for future versions.

    Can I use multiple scenes at once?

    Yes, use SceneManager to push/pop scenes. This is useful for menus and pause screens.

    Getting Help

    Where can I get help?

    • Documentation: This documentation site
    • Examples: Study example games in the samples project
    • Discord: Community Discord server
    • GitHub: Open an issue for bugs

    How do I report a bug?

    Create a detailed bug report with: - Platform (ESP32 or Native) - Hardware details - Minimal reproduction code - Error messages - Expected vs actual behavior

    Can I contribute?

    Yes! PixelRoot32 is open source. Check the main repository for contribution guidelines.

    See Also


    Can't find your question? Check the Troubleshooting guide or ask on the Discord server.

    \ No newline at end of file +

    Hardware

    Which ESP32 board should I use?

    Any ESP32 board works. Common choices: - ESP32-WROOM-32 - ESP32-WROVER (more RAM) - ESP32-DevKit

    Which display should I use?

    Popular choices: - ST7789: 240x240, good quality - ST7735: 128x128, smaller/cheaper - ILI9341: 240x320, larger

    See Platforms and Drivers.

    How many buttons do I need?

    Minimum: 4 (UP, DOWN, LEFT, RIGHT) Recommended: 6 (add A and B buttons) More buttons can be added if needed.

    Can I use analog joysticks?

    Not directly supported. You can read analog pins manually and convert to digital input, but the engine expects digital buttons.

    Troubleshooting

    My display is blank. What's wrong?

    1. Check wiring connections
    2. Verify pin numbers in platformio.ini
    3. Lower SPI frequency
    4. Check display type matches hardware
    5. Verify power supply

    See Troubleshooting.

    Buttons don't work. Why?

    1. Check button wiring
    2. Verify pin numbers in InputConfig
    3. Check pull-up/pull-down resistors
    4. Ensure InputManager is being updated
    5. Test with isButtonDown() vs isButtonPressed()

    Audio is distorted. How do I fix it?

    1. Lower volume levels
    2. Reduce sample rate (try 11025 Hz)
    3. Check for too many simultaneous sounds
    4. Verify hardware connections
    5. Check power supply

    Game crashes randomly. What's happening?

    Common causes: - Out of memory - Too many entities - Infinite loops - Stack overflow - Watchdog timeout

    See Troubleshooting for debugging techniques.

    Advanced

    Can I extend the engine?

    Yes, PixelRoot32 is designed to be extensible. You can: - Create custom display drivers - Create custom audio backends - Extend existing systems

    See Extensibility.

    Can I use 3D graphics?

    No, PixelRoot32 is 2D-only. It's designed for sprite-based 2D games.

    Can I add networking?

    Not currently supported. The engine focuses on single-player games.

    Can I save game data?

    Not currently supported. A save system is planned for future versions.

    Can I use multiple scenes at once?

    Yes, use SceneManager to push/pop scenes. This is useful for menus and pause screens.

    Getting Help

    Where can I get help?

    • Documentation: This documentation site
    • Examples: Study example games in the samples project
    • Discord: Community Discord server
    • GitHub: Open an issue for bugs

    How do I report a bug?

    Create a detailed bug report with: - Platform (ESP32 or Native) - Hardware details - Minimal reproduction code - Error messages - Expected vs actual behavior

    Can I contribute?

    Yes! PixelRoot32 is open source. Check the main repository for contribution guidelines.

    See Also


    Can't find your question? Check the Troubleshooting guide or ask on the Discord server.

    \ No newline at end of file diff --git a/site/resources/limitations_and_considerations/index.html b/site/resources/limitations_and_considerations/index.html index 824806d..8a56e97 100644 --- a/site/resources/limitations_and_considerations/index.html +++ b/site/resources/limitations_and_considerations/index.html @@ -1 +1 @@ - Limitations and Considerations - PixelRoot32 Documentation

    Limitations and Considerations

    This document honestly documents what PixelRoot32 can and cannot do, helping you make informed decisions about using the engine.

    Hardware Limitations (ESP32)

    Memory Constraints

    RAM: - Available: ~320KB total (varies by ESP32 model) - Heap: Limited and fragmented over time - Stack: ~8KB, avoid large stack allocations - Impact: Limits entity count, sprite data, and dynamic allocation

    Flash: - Available: 4MB+ (varies by model) - Usage: Program code, sprite data, assets - Impact: Large games may approach limits

    Recommendations: - Use object pooling - Store data in flash (const/constexpr) - Avoid dynamic allocation in game loop - Keep entity count low

    CPU Limitations

    Performance: - Clock Speed: 240MHz (typically) - Single-threaded: One core handles everything - Target FPS: 30-60 FPS (depends on complexity) - Frame Budget: ~16-33ms per frame at 60 FPS

    Impact: - Complex games may struggle - Many entities reduce performance - Expensive calculations hurt FPS

    Recommendations: - Optimize rendering - Reduce entity count - Cache calculations - Profile on hardware

    Display Limitations

    Supported Displays: - TFT displays via SPI (ST7735, ST7789, ILI9341, etc.) - Limited to SPI displays - Resolution typically 128x128 to 320x240

    Constraints: - SPI communication speed limits - Display initialization complexity - Power consumption

    Audio Limitations

    Hardware: - Internal DAC: Lower quality, simple setup - I2S: Higher quality, requires external DAC - Sample rates: 11025 Hz (DAC) or 22050 Hz (I2S)

    Constraints: - 4 channels total (2 Pulse, 1 Triangle, 1 Noise) - Music uses one channel - Limited simultaneous sounds - Quality limited by hardware

    Software Limitations

    Entity System

    MAX_ENTITIES = 32 per scene - Hard limit, cannot be changed easily - Applies to all entities (actors, UI, particles, etc.) - Must manage entity count carefully

    Workarounds: - Use object pooling - Reuse entities - Disable entities instead of removing - Combine multiple entities into one

    No RTTI (Runtime Type Information)

    Impact: - Cannot use dynamic_cast in most code - Type checking must be done manually - Limits polymorphism patterns

    Alternatives: - Use virtual functions - Manual type checking - Tag-based systems

    No Exceptions in Critical Code

    Impact: - Cannot use try/catch in game loop - Error handling must be explicit - Crashes instead of exceptions

    Best Practices: - Validate inputs - Check return values - Use assertions for debugging - Handle errors explicitly

    No Dynamic Allocation in Game Loop

    Impact: - Cannot use new/delete during gameplay - Must pre-allocate resources - Limits flexibility

    Solutions: - Object pooling - Pre-allocation in init() - Static buffers - Fixed-size arrays

    No Advanced Features

    Not Supported: - 3D graphics - Shaders - Advanced physics (joints, constraints) - Networking - File system (ESP32) - Advanced audio effects

    Focus: - 2D sprite-based games - Simple physics - Retro-style games - Embedded-friendly features

    Experimental Features

    2bpp Sprites

    Status: Experimental - Requires PIXELROOT32_ENABLE_2BPP_SPRITES flag - May have bugs or limitations - Not fully tested

    Use with caution: - Test thoroughly - May change in future versions - Report issues if found

    4bpp Sprites

    Status: Experimental - Requires PIXELROOT32_ENABLE_4BPP_SPRITES flag - More experimental than 2bpp - Higher memory usage

    Use with caution: - Test extensively - Monitor memory usage - May be unstable

    Scene Arena

    Status: Experimental - Requires PIXELROOT32_ENABLE_SCENE_ARENA flag - Alternative memory management - May have bugs

    Recommendations: - Use object pooling instead (more stable) - Test thoroughly if using - May be removed or changed

    Unsupported Features (Current)

    Planned but Not Available

    • u8g2 Driver: Alternative display driver (planned)
    • Music Compiler: Tool to convert music files (planned)
    • Tilemap Compiler: Tool to create tilemaps (planned)
    • Save System: Persistent storage system (planned)
    • Spatial Partitioning: Advanced collision optimization (planned)

    Not Planned

    • 3D Graphics: 2D-only engine
    • Networking: No network support
    • File System: No file I/O on ESP32
    • Advanced Audio: NES-like audio only
    • Scripting: No Lua/JavaScript support

    Best Practices for ESP32

    Memory Management

    • Pre-allocate: All resources in init()
    • Object pooling: Reuse entities
    • Flash storage: Use const/constexpr for data
    • Avoid strings: Use static buffers
    • Monitor usage: Check heap regularly

    Performance

    • Limit entities: Stay well below MAX_ENTITIES
    • Optimize rendering: Use culling, batching
    • Cache calculations: Avoid repeated work
    • Profile on hardware: PC performance ≠ ESP32

    Development

    • Test on hardware: Don't rely only on Native
    • Start simple: Add complexity gradually
    • Monitor memory: Watch for leaks
    • Optimize incrementally: Profile and optimize

    What PixelRoot32 IS Good For

    Retro-style 2D gamesArcade gamesPuzzle gamesPlatformersShootersEducational projectsPrototypingEmbedded game development

    What PixelRoot32 is NOT Good For

    3D gamesComplex physics simulationsLarge open worldsGames requiring many entitiesGames with complex graphicsNetwork multiplayerGames requiring file I/O

    Making Informed Decisions

    Before Starting a Project

    1. Assess requirements: Does PixelRoot32 fit?
    2. Check limitations: Can you work within constraints?
    3. Plan architecture: Design around limitations
    4. Test early: Verify on hardware early

    If Limitations Are a Problem

    Consider alternatives: - Full game engines (Unity, Godot) for complex games - Custom solutions for specific needs - Different hardware for more resources

    Or work within limits: - Simplify game design - Optimize aggressively - Use creative solutions

    Version Compatibility

    Current Version

    • Engine Version: 0.2.0-dev
    • API Stability: May change
    • Breaking Changes: Possible in future versions

    Recommendations: - Pin exact version in platformio.ini - Don't use ^ or fuzzy versioning - Test after engine updates - Review changelog

    Honest Assessment

    PixelRoot32 is designed for: - Simple to medium complexity games - Retro/arcade style - ESP32 hardware constraints - Rapid development

    It is not designed for: - AAA game complexity - Modern graphics - Large-scale games - Unlimited resources

    See Also


    Remember: Understanding limitations helps you build better games within PixelRoot32's capabilities.

    \ No newline at end of file + Limitations and Considerations - PixelRoot32 Documentation

    Limitations and Considerations

    This document honestly documents what PixelRoot32 can and cannot do, helping you make informed decisions about using the engine.

    Hardware Limitations (ESP32)

    Memory Constraints

    RAM: - Available: ~320KB total (varies by ESP32 model) - Heap: Limited and fragmented over time - Stack: ~8KB, avoid large stack allocations - Impact: Limits entity count, sprite data, and dynamic allocation

    Flash: - Available: 4MB+ (varies by model) - Usage: Program code, sprite data, assets - Impact: Large games may approach limits

    Recommendations: - Use object pooling - Store data in flash (const/constexpr) - Avoid dynamic allocation in game loop - Keep entity count low

    CPU Limitations

    Performance: - Clock Speed: 240MHz (typically) - Single-threaded: One core handles everything - Target FPS: 30-60 FPS (depends on complexity) - Frame Budget: ~16-33ms per frame at 60 FPS

    Impact: - Complex games may struggle - Many entities reduce performance - Expensive calculations hurt FPS

    Recommendations: - Optimize rendering - Reduce entity count - Cache calculations - Profile on hardware

    Display Limitations

    Supported Displays: - TFT displays via SPI (ST7735, ST7789, ILI9341, etc.) - Limited to SPI displays - Resolution typically 128x128 to 320x240

    Constraints: - SPI communication speed limits - Display initialization complexity - Power consumption

    Audio Limitations

    Hardware: - Internal DAC: Lower quality, simple setup - I2S: Higher quality, requires external DAC - Sample rates: 11025 Hz (DAC) or 22050 Hz (I2S)

    Constraints: - 4 channels total (2 Pulse, 1 Triangle, 1 Noise) - Music uses one channel - Limited simultaneous sounds - Quality limited by hardware

    Software Limitations

    Entity System

    MAX_ENTITIES = 32 per scene - Hard limit, cannot be changed easily - Applies to all entities (actors, UI, particles, etc.) - Must manage entity count carefully

    Workarounds: - Use object pooling - Reuse entities - Disable entities instead of removing - Combine multiple entities into one

    No RTTI (Runtime Type Information)

    Impact: - Cannot use dynamic_cast in most code - Type checking must be done manually - Limits polymorphism patterns

    Alternatives: - Use virtual functions - Manual type checking - Tag-based systems

    No Exceptions in Critical Code

    Impact: - Cannot use try/catch in game loop - Error handling must be explicit - Crashes instead of exceptions

    Best Practices: - Validate inputs - Check return values - Use assertions for debugging - Handle errors explicitly

    No Dynamic Allocation in Game Loop

    Impact: - Cannot use new/delete during gameplay - Must pre-allocate resources - Limits flexibility

    Solutions: - Object pooling - Pre-allocation in init() - Static buffers - Fixed-size arrays

    No Advanced Features

    Not Supported: - 3D graphics - Shaders - Advanced physics (joints, constraints) - Networking - File system (ESP32) - Advanced audio effects

    Focus: - 2D sprite-based games - Simple physics - Retro-style games - Embedded-friendly features

    Experimental Features

    2bpp Sprites

    Status: Experimental - Requires PIXELROOT32_ENABLE_2BPP_SPRITES flag - May have bugs or limitations - Not fully tested

    Use with caution: - Test thoroughly - May change in future versions - Report issues if found

    4bpp Sprites

    Status: Experimental - Requires PIXELROOT32_ENABLE_4BPP_SPRITES flag - More experimental than 2bpp - Higher memory usage

    Use with caution: - Test extensively - Monitor memory usage - May be unstable

    Scene Arena

    Status: Experimental - Requires PIXELROOT32_ENABLE_SCENE_ARENA flag - Alternative memory management - May have bugs

    Recommendations: - Use object pooling instead (more stable) - Test thoroughly if using - May be removed or changed

    Unsupported Features (Current)

    Planned but Not Available

    • u8g2 Driver: Alternative display driver (planned)
    • Music Compiler: Tool to convert music files (planned)
    • Tilemap Compiler: Tool to create tilemaps (planned)
    • Save System: Persistent storage system (planned)
    • Spatial Partitioning: Advanced collision optimization (planned)

    Not Planned

    • 3D Graphics: 2D-only engine
    • Networking: No network support
    • File System: No file I/O on ESP32
    • Advanced Audio: NES-like audio only
    • Scripting: No Lua/JavaScript support

    Best Practices for ESP32

    Memory Management

    • Pre-allocate: All resources in init()
    • Object pooling: Reuse entities
    • Flash storage: Use const/constexpr for data
    • Avoid strings: Use static buffers
    • Monitor usage: Check heap regularly

    Performance

    • Limit entities: Stay well below MAX_ENTITIES
    • Optimize rendering: Use culling, batching
    • Cache calculations: Avoid repeated work
    • Profile on hardware: PC performance ≠ ESP32

    Development

    • Test on hardware: Don't rely only on Native
    • Start simple: Add complexity gradually
    • Monitor memory: Watch for leaks
    • Optimize incrementally: Profile and optimize

    What PixelRoot32 IS Good For

    Retro-style 2D gamesArcade gamesPuzzle gamesPlatformersShootersEducational projectsPrototypingEmbedded game development

    What PixelRoot32 is NOT Good For

    3D gamesComplex physics simulationsLarge open worldsGames requiring many entitiesGames with complex graphicsNetwork multiplayerGames requiring file I/O

    Making Informed Decisions

    Before Starting a Project

    1. Assess requirements: Does PixelRoot32 fit?
    2. Check limitations: Can you work within constraints?
    3. Plan architecture: Design around limitations
    4. Test early: Verify on hardware early

    If Limitations Are a Problem

    Consider alternatives: - Full game engines (Unity, Godot) for complex games - Custom solutions for specific needs - Different hardware for more resources

    Or work within limits: - Simplify game design - Optimize aggressively - Use creative solutions

    Version Compatibility

    Current Version

    • Engine Version: 0.2.0-dev
    • API Stability: May change
    • Breaking Changes: Possible in future versions

    Recommendations: - Pin exact version in platformio.ini - Don't use ^ or fuzzy versioning - Test after engine updates - Review changelog

    Honest Assessment

    PixelRoot32 is designed for: - Simple to medium complexity games - Retro/arcade style - ESP32 hardware constraints - Rapid development

    It is not designed for: - AAA game complexity - Modern graphics - Large-scale games - Unlimited resources

    See Also


    Remember: Understanding limitations helps you build better games within PixelRoot32's capabilities.

    \ No newline at end of file diff --git a/site/resources/troubleshooting/index.html b/site/resources/troubleshooting/index.html index d495c7d..ef0a1d7 100644 --- a/site/resources/troubleshooting/index.html +++ b/site/resources/troubleshooting/index.html @@ -1,4 +1,4 @@ - Troubleshooting - PixelRoot32 Documentation

    Troubleshooting

    This guide helps you diagnose and fix common issues when developing with PixelRoot32.

    Compilation Problems

    Common Compilation Errors

    Error: Library not found

    Solution: Ensure PixelRoot32-Game-Engine is properly installed
    + Troubleshooting - PixelRoot32 Documentation      

    Troubleshooting

    This guide helps you diagnose and fix common issues when developing with PixelRoot32.

    Compilation Problems

    Common Compilation Errors

    Error: Library not found

    Solution: Ensure PixelRoot32-Game-Engine is properly installed
     - Check platformio.ini has correct lib_deps
     - Verify library version matches (use exact version, not ^)
     - Try: pio lib install
    @@ -71,4 +71,4 @@
     // Usage
     DEBUG_LOG("Entity created");
     DEBUG_LOG("Collision detected");
    -

    Getting Help

    If you can't resolve an issue:

    1. Check documentation: Review relevant guides
    2. Search examples: Look at example games
    3. Review code: Check engine source code
    4. Community: Ask on Discord or GitHub
    5. Report issue: Create detailed bug report

    Bug Report Template

    When reporting issues, include:

    • Platform: ESP32 or Native
    • Hardware: Display type, ESP32 model
    • Code: Minimal reproduction code
    • Error messages: Full error output
    • Expected behavior: What should happen
    • Actual behavior: What actually happens
    • Steps to reproduce: How to trigger the issue

    See Also


    Note: Many issues are configuration-related. Double-check your setup before assuming a bug.

    \ No newline at end of file +

    Getting Help

    If you can't resolve an issue:

    1. Check documentation: Review relevant guides
    2. Search examples: Look at example games
    3. Review code: Check engine source code
    4. Community: Ask on Discord or GitHub
    5. Report issue: Create detailed bug report

    Bug Report Template

    When reporting issues, include:

    • Platform: ESP32 or Native
    • Hardware: Display type, ESP32 model
    • Code: Minimal reproduction code
    • Error messages: Full error output
    • Expected behavior: What should happen
    • Actual behavior: What actually happens
    • Steps to reproduce: How to trigger the issue

    See Also


    Note: Many issues are configuration-related. Double-check your setup before assuming a bug.

    \ No newline at end of file diff --git a/site/search/search_index.json b/site/search/search_index.json index 05f72a8..f6b1279 100644 --- a/site/search/search_index.json +++ b/site/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"PixelRoot32 Documentation","text":"

    PixelRoot32 is a lightweight 2D game engine designed for ESP32 and native desktop targets. This site provides official, versioned documentation with clear guides, conceptual explanations, API references, and complete examples to help you build games efficiently.

    "},{"location":"#quick-links","title":"Quick Links","text":"
    • What is PixelRoot32? - Start here to understand the engine
    • Your First Project - Get up and running quickly
    • Fundamental Concepts - Learn the core concepts
    • Manual - Complete user guide
    • API Reference - Complete API documentation
    • Examples - Complete game examples
    • Tools - Available tools
    • FAQ - FAQ and troubleshooting
    "},{"location":"#getting-started","title":"Getting Started","text":"

    New to PixelRoot32? Follow this learning path:

    1. What is PixelRoot32? - Understand what the engine is and what it can do
    2. Why PixelRoot32? - Learn the advantages and use cases
    3. Fundamental Concepts - Learn the core architecture concepts
    4. Your First Project - Create and run your first project
    "},{"location":"#about-this-documentation","title":"About This Documentation","text":"
    • Professional technical English across all pages
    • Search-enabled, mobile-friendly UI
    • Versioned with mike (stable/dev/experimental)
    • Cross-linked concepts, API, and examples
    • Progressive learning path from basics to advanced topics
    "},{"location":"api_reference/audio/audio_config/","title":"AudioConfig","text":"

    Configuration for the Audio subsystem.

    "},{"location":"api_reference/audio/audio_config/#description","title":"Description","text":"

    AudioConfig is a simple struct that holds configuration settings for the audio system, including the audio backend and sample rate. It is passed to AudioEngine during construction.

    "},{"location":"api_reference/audio/audio_config/#namespace","title":"Namespace","text":"
    namespace pixelroot32::audio {\n    struct AudioConfig {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/audio/audio_config/#structure","title":"Structure","text":""},{"location":"api_reference/audio/audio_config/#audiobackend-backend","title":"AudioBackend* backend","text":"

    Pointer to the platform-specific audio backend implementation.

    Type: AudioBackend*

    Access: Read-write

    Default: nullptr

    Notes: - Must be set to a valid backend instance - Backend is platform-specific: - ESP32: ESP32_DAC_AudioBackend or ESP32_I2S_AudioBackend - Native: SDL2_AudioBackend - Backend manages the actual audio hardware/API

    Example:

    #ifdef PLATFORM_ESP32\n    pixelroot32::drivers::esp32::ESP32_DAC_AudioBackend dacBackend;\n    audioConfig.backend = &dacBackend;\n#elif PLATFORM_NATIVE\n    pixelroot32::drivers::native::SDL2_AudioBackend sdlBackend;\n    audioConfig.backend = &sdlBackend;\n#endif\n

    "},{"location":"api_reference/audio/audio_config/#int-samplerate","title":"int sampleRate","text":"

    Desired sample rate in Hz.

    Type: int

    Access: Read-write

    Default: 22050

    Notes: - Common values: 11025, 22050, 44100 - Lower rates use less CPU and memory (better for ESP32) - Higher rates provide better quality - Must match backend capabilities

    Example:

    audioConfig.sampleRate = 11025;  // Lower quality, less CPU (ESP32)\naudioConfig.sampleRate = 22050;  // Balanced (default)\naudioConfig.sampleRate = 44100; // Higher quality (Native)\n

    "},{"location":"api_reference/audio/audio_config/#constructors","title":"Constructors","text":""},{"location":"api_reference/audio/audio_config/#audioconfigaudiobackend-backend-nullptr-int-samplerate-22050","title":"AudioConfig(AudioBackend* backend = nullptr, int sampleRate = 22050)","text":"

    Default constructor.

    Parameters: - backend (AudioBackend*, optional): Pointer to the audio backend implementation. Default: nullptr - sampleRate (int, optional): Desired sample rate in Hz. Default: 22050

    Example:

    // Default construction\npixelroot32::audio::AudioConfig audioConfig;\n\n// With backend\npixelroot32::drivers::esp32::ESP32_DAC_AudioBackend dacBackend;\npixelroot32::audio::AudioConfig audioConfig(&dacBackend, 11025);\n

    "},{"location":"api_reference/audio/audio_config/#usage-example","title":"Usage Example","text":""},{"location":"api_reference/audio/audio_config/#esp32-with-dac-backend","title":"ESP32 with DAC Backend","text":"
    #ifdef PLATFORM_ESP32\n#include \"drivers/esp32/ESP32_DAC_AudioBackend.h\"\n\npixelroot32::drivers::esp32::ESP32_DAC_AudioBackend dacBackend;\n\npixelroot32::audio::AudioConfig audioConfig;\naudioConfig.backend = &dacBackend;\naudioConfig.sampleRate = 11025;  // Lower rate for ESP32\n\npixelroot32::audio::AudioEngine audioEngine(audioConfig);\naudioEngine.init();\n#endif\n
    "},{"location":"api_reference/audio/audio_config/#esp32-with-i2s-backend","title":"ESP32 with I2S Backend","text":"
    #ifdef PLATFORM_ESP32\n#include \"drivers/esp32/ESP32_I2S_AudioBackend.h\"\n\npixelroot32::drivers::esp32::ESP32_I2S_AudioBackend i2sBackend;\n\npixelroot32::audio::AudioConfig audioConfig;\naudioConfig.backend = &i2sBackend;\naudioConfig.sampleRate = 22050;  // Higher quality with I2S\n\npixelroot32::audio::AudioEngine audioEngine(audioConfig);\naudioEngine.init();\n#endif\n
    "},{"location":"api_reference/audio/audio_config/#native-with-sdl2-backend","title":"Native with SDL2 Backend","text":"
    #ifdef PLATFORM_NATIVE\n#include \"drivers/native/SDL2_AudioBackend.h\"\n\npixelroot32::drivers::native::SDL2_AudioBackend sdlBackend;\n\npixelroot32::audio::AudioConfig audioConfig;\naudioConfig.backend = &sdlBackend;\naudioConfig.sampleRate = 44100;  // High quality for PC\n\npixelroot32::audio::AudioEngine audioEngine(audioConfig);\naudioEngine.init();\n#endif\n
    "},{"location":"api_reference/audio/audio_config/#complete-engine-setup","title":"Complete Engine Setup","text":"
    #include \"core/Engine.h\"\n#include \"graphics/DisplayConfig.h\"\n#include \"input/InputConfig.h\"\n#include \"audio/AudioConfig.h\"\n\nvoid setup() {\n    // Display config\n    pixelroot32::graphics::DisplayConfig displayConfig;\n    displayConfig.width = 128;\n    displayConfig.height = 128;\n\n    // Input config\n    pixelroot32::input::InputConfig inputConfig;\n    // ... configure input\n\n    // Audio config\n    #ifdef PLATFORM_ESP32\n        pixelroot32::drivers::esp32::ESP32_DAC_AudioBackend dacBackend;\n        pixelroot32::audio::AudioConfig audioConfig(&dacBackend, 11025);\n    #elif PLATFORM_NATIVE\n        pixelroot32::drivers::native::SDL2_AudioBackend sdlBackend;\n        pixelroot32::audio::AudioConfig audioConfig(&sdlBackend, 44100);\n    #endif\n\n    // Create engine with all configs\n    pixelroot32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n    engine.init();\n    engine.run();\n}\n
    "},{"location":"api_reference/audio/audio_config/#platform-specific-considerations","title":"Platform-Specific Considerations","text":""},{"location":"api_reference/audio/audio_config/#esp32-dac-backend","title":"ESP32 DAC Backend","text":"
    • Sample rate: 11025 Hz recommended (lower CPU usage)
    • Quality: Lower quality, but simple setup
    • Pin: Uses GPIO 25 or 26
    • Hardware: Requires simple amplifier circuit
    "},{"location":"api_reference/audio/audio_config/#esp32-i2s-backend","title":"ESP32 I2S Backend","text":"
    • Sample rate: 22050 Hz recommended
    • Quality: Higher quality than DAC
    • Pins: Requires I2S pins (BCLK, LRCK, DOUT)
    • Hardware: Requires external I2S DAC
    "},{"location":"api_reference/audio/audio_config/#native-sdl2-backend","title":"Native SDL2 Backend","text":"
    • Sample rate: 44100 Hz typical
    • Quality: High quality
    • Setup: Requires SDL2 library installed
    • Platforms: Windows, Linux, macOS
    "},{"location":"api_reference/audio/audio_config/#performance-considerations","title":"Performance Considerations","text":"
    • Sample rate: Lower rates use less CPU and memory
    • Backend choice: DAC is simpler but lower quality than I2S
    • Buffer size: Configured in backend, affects latency vs stability
    "},{"location":"api_reference/audio/audio_config/#see-also","title":"See Also","text":"
    • AudioEngine - Audio playback engine
    • Manual - Audio
    • Manual - Platforms and Drivers
    • API Overview
    "},{"location":"api_reference/audio/audio_engine/","title":"AudioEngine","text":"

    Core class for the NES-like audio subsystem.

    "},{"location":"api_reference/audio/audio_engine/#description","title":"Description","text":"

    AudioEngine manages the audio channels (Pulse, Triangle, Noise), mixes their output, and provides the audio stream to the backend. It implements a NES-like audio system with 4 fixed channels: 2 Pulse channels, 1 Triangle channel, and 1 Noise channel.

    The engine is event-driven: you trigger sound effects via playEvent(), and the engine automatically manages channel allocation and playback.

    "},{"location":"api_reference/audio/audio_engine/#namespace","title":"Namespace","text":"
    namespace pixelroot32::audio {\n    class AudioEngine {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/audio/audio_engine/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Engine (manages audio engine instance)
    "},{"location":"api_reference/audio/audio_engine/#constructors","title":"Constructors","text":""},{"location":"api_reference/audio/audio_engine/#audioengineconst-audioconfig-config","title":"AudioEngine(const AudioConfig& config)","text":"

    Constructs the AudioEngine with the given configuration.

    Parameters: - config (const AudioConfig&): Configuration struct containing the backend and parameters (sample rate, etc.)

    Example:

    #include \"audio/AudioEngine.h\"\n#include \"audio/AudioConfig.h\"\n\npixelroot32::audio::AudioConfig audioConfig;\naudioConfig.backend = &audioBackend;  // Platform-specific backend\naudioConfig.sampleRate = 22050;       // 22.05 kHz for retro feel\n\npixelroot32::audio::AudioEngine audioEngine(audioConfig);\naudioEngine.init();\n

    "},{"location":"api_reference/audio/audio_engine/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/audio/audio_engine/#void-init","title":"void init()","text":"

    Initializes the audio subsystem and the backend.

    Returns: - void

    Notes: - Must be called after construction and before use - Initializes the platform-specific audio backend - Safe to call multiple times (idempotent) - Typically called automatically by Engine::init()

    Example:

    AudioEngine audioEngine(audioConfig);\naudioEngine.init();  // Initialize before use\n

    "},{"location":"api_reference/audio/audio_engine/#void-updateunsigned-long-deltatime","title":"void update(unsigned long deltaTime)","text":"

    Updates the audio state based on game time.

    Parameters: - deltaTime (unsigned long): Time elapsed since last frame in milliseconds

    Returns: - void

    Notes: - Should be called from the main game loop (typically via Engine::update()) - Updates channel lifetimes and durations - Automatically stops channels when their duration expires - Must be called every frame for proper audio timing

    Example:

    void update(unsigned long deltaTime) override {\n    // Update audio (called automatically by Engine)\n    engine.getAudioEngine().update(deltaTime);\n\n    // Your game logic...\n}\n

    "},{"location":"api_reference/audio/audio_engine/#void-generatesamplesint16_t-stream-int-length","title":"void generateSamples(int16_t* stream, int length)","text":"

    Fills the provided buffer with mixed audio samples.

    Parameters: - stream (int16_t*): Pointer to the buffer to fill - length (int): Number of samples to generate

    Returns: - void

    Notes: - This method is typically called by the AudioBackend from an audio callback or task - Not usually called directly by game code - Generates 16-bit signed integer PCM samples - Mixes all active channels into a mono stream

    Advanced Usage:

    // Typically not called directly, but if implementing custom backend:\nint16_t buffer[512];\naudioEngine.generateSamples(buffer, 512);\n

    "},{"location":"api_reference/audio/audio_engine/#void-playeventconst-audioevent-event","title":"void playEvent(const AudioEvent& event)","text":"

    Triggers a one-shot sound effect.

    Parameters: - event (const AudioEvent&): The audio event to play

    Returns: - void

    Notes: - Automatically finds an available channel of the correct type - If no channel is available, the event may be dropped (no error) - Events are fire-and-forget (no need to track playback) - Use for sound effects, not background music

    Example:

    // Play a jump sound\npixelroot32::audio::AudioEvent jumpSound{};\njumpSound.type = pixelroot32::audio::WaveType::PULSE;\njumpSound.frequency = 800.0f;\njumpSound.duration = 0.1f;\njumpSound.volume = 0.7f;\njumpSound.duty = 0.5f;\n\nauto& audio = engine.getAudioEngine();\naudio.playEvent(jumpSound);\n\n// Play an explosion sound\npixelroot32::audio::AudioEvent explosion{};\nexplosion.type = pixelroot32::audio::WaveType::NOISE;\nexplosion.frequency = 1000.0f;\nexplosion.duration = 0.3f;\nexplosion.volume = 0.9f;\n\naudio.playEvent(explosion);\n

    "},{"location":"api_reference/audio/audio_engine/#void-setmastervolumefloat-volume","title":"void setMasterVolume(float volume)","text":"

    Sets the master volume for all audio output.

    Parameters: - volume (float): Volume level (0.0 = silent, 1.0 = full volume)

    Returns: - void

    Notes: - Affects all channels and events - Clamped to [0.0, 1.0] range - Use for volume control menus or mute functionality

    Example:

    auto& audio = engine.getAudioEngine();\naudio.setMasterVolume(0.5f);  // 50% volume\naudio.setMasterVolume(0.0f);  // Mute\naudio.setMasterVolume(1.0f);  // Full volume\n

    "},{"location":"api_reference/audio/audio_engine/#float-getmastervolume-const","title":"float getMasterVolume() const","text":"

    Gets the current master volume.

    Returns: - float: Current master volume (0.0 to 1.0)

    Example:

    float currentVolume = audioEngine.getMasterVolume();\n

    "},{"location":"api_reference/audio/audio_engine/#audio-channels","title":"Audio Channels","text":"

    The engine manages 4 fixed channels:

    1. Channel 0: Pulse wave
    2. Channel 1: Pulse wave
    3. Channel 2: Triangle wave
    4. Channel 3: Noise wave

    Notes: - Channels are automatically allocated when playing events - If all channels of a type are busy, new events may be dropped - Background music typically uses one channel (via MusicPlayer)

    "},{"location":"api_reference/audio/audio_engine/#usage-example","title":"Usage Example","text":"
    #include \"audio/AudioEngine.h\"\n#include \"audio/AudioConfig.h\"\n\nclass MyScene : public pixelroot32::core::Scene {\nprivate:\n    void playJumpSound() {\n        auto& audio = engine.getAudioEngine();\n\n        pixelroot32::audio::AudioEvent sound{};\n        sound.type = pixelroot32::audio::WaveType::PULSE;\n        sound.frequency = 800.0f;\n        sound.duration = 0.1f;\n        sound.volume = 0.7f;\n        sound.duty = 0.5f;\n\n        audio.playEvent(sound);\n    }\n\n    void playHitSound() {\n        auto& audio = engine.getAudioEngine();\n\n        pixelroot32::audio::AudioEvent sound{};\n        sound.type = pixelroot32::audio::WaveType::NOISE;\n        sound.frequency = 500.0f;\n        sound.duration = 0.05f;\n        sound.volume = 0.5f;\n\n        audio.playEvent(sound);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Audio is updated automatically by Engine\n        // Just play events when needed\n        if (playerJumped) {\n            playJumpSound();\n            playerJumped = false;\n        }\n    }\n};\n
    "},{"location":"api_reference/audio/audio_engine/#performance-considerations","title":"Performance Considerations","text":"
    • Channel limit: Only 4 channels total; plan sound effects accordingly
    • Event dropping: If all channels are busy, new events are silently dropped
    • Update frequency: update() must be called every frame for proper timing
    • Sample generation: generateSamples() is called by backend at audio rate (not game rate)
    "},{"location":"api_reference/audio/audio_engine/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Sample rate: Lower sample rates (11025 Hz) use less CPU and memory
    • Backend choice: DAC backend is simpler but lower quality than I2S
    • Buffer size: Larger buffers reduce underruns but increase latency
    • Channel management: Limit simultaneous sounds to avoid channel conflicts
    "},{"location":"api_reference/audio/audio_engine/#see-also","title":"See Also","text":"
    • AudioConfig - Audio configuration
    • AudioTypes - Audio data structures
    • MusicPlayer - Background music playback
    • Manual - Audio
    • API Overview
    "},{"location":"api_reference/audio/audio_types/","title":"Audio Types","text":"

    Data structures and types for the audio system.

    "},{"location":"api_reference/audio/audio_types/#description","title":"Description","text":"

    This document describes the data structures used by the audio system, including wave types, audio events, and channel state.

    "},{"location":"api_reference/audio/audio_types/#namespace","title":"Namespace","text":"
    namespace pixelroot32::audio {\n    // Types and structures\n}\n
    "},{"location":"api_reference/audio/audio_types/#wavetype-enum","title":"WaveType Enum","text":"

    Defines the types of waveforms available.

    Values: - WaveType::PULSE: Pulse wave (square wave with variable duty cycle) - WaveType::TRIANGLE: Triangle wave (smooth, melodic) - WaveType::NOISE: Noise wave (random, percussive)

    Example:

    pixelroot32::audio::WaveType wave = pixelroot32::audio::WaveType::PULSE;\n

    "},{"location":"api_reference/audio/audio_types/#audioevent-structure","title":"AudioEvent Structure","text":"

    A fire-and-forget sound event triggered by the game.

    Members: - WaveType type: Type of waveform to use - float frequency: Frequency in Hz - float duration: Duration in seconds - float volume: Volume level (0.0 to 1.0) - float duty: Duty cycle for pulse wave (0.0 to 1.0, pulse only)

    Example:

    pixelroot32::audio::AudioEvent jumpSound{};\njumpSound.type = pixelroot32::audio::WaveType::PULSE;\njumpSound.frequency = 800.0f;\njumpSound.duration = 0.1f;\njumpSound.volume = 0.7f;\njumpSound.duty = 0.5f;\n\nauto& audio = engine.getAudioEngine();\naudio.playEvent(jumpSound);\n

    "},{"location":"api_reference/audio/audio_types/#common-sound-effects","title":"Common Sound Effects","text":"

    Jump Sound:

    pixelroot32::audio::AudioEvent jump{};\njump.type = pixelroot32::audio::WaveType::PULSE;\njump.frequency = 800.0f;\njump.duration = 0.1f;\njump.volume = 0.7f;\njump.duty = 0.5f;\n

    Hit Sound:

    pixelroot32::audio::AudioEvent hit{};\nhit.type = pixelroot32::audio::WaveType::NOISE;\nhit.frequency = 500.0f;\nhit.duration = 0.05f;\nhit.volume = 0.5f;\n

    Collect Sound:

    pixelroot32::audio::AudioEvent collect{};\ncollect.type = pixelroot32::audio::WaveType::TRIANGLE;\ncollect.frequency = 1000.0f;\ncollect.duration = 0.15f;\ncollect.volume = 0.6f;\n

    Explosion:

    pixelroot32::audio::AudioEvent explosion{};\nexplosion.type = pixelroot32::audio::WaveType::NOISE;\nexplosion.frequency = 200.0f;\nexplosion.duration = 0.3f;\nexplosion.volume = 0.9f;\n

    "},{"location":"api_reference/audio/audio_types/#audiochannel-structure","title":"AudioChannel Structure","text":"

    Represents the internal state of a single audio channel.

    Members: - bool enabled: Whether the channel is active - WaveType type: Type of waveform - float frequency: Current frequency in Hz - float phase: Current phase (0.0 to 1.0) - float phaseIncrement: Pre-calculated phase increment - float volume: Current volume (0.0 to 1.0) - float targetVolume: Target volume for envelopes - float dutyCycle: Duty cycle for pulse wave (0.0 to 1.0) - uint16_t noiseRegister: LFSR state for noise generation - unsigned long durationMs: Total duration in milliseconds - unsigned long remainingMs: Remaining duration in milliseconds

    Methods: - void reset(): Resets the channel to inactive state

    Notes: - Internal structure, typically not accessed directly - Managed automatically by AudioEngine - 4 channels total: 2 Pulse, 1 Triangle, 1 Noise

    "},{"location":"api_reference/audio/audio_types/#frequency-reference","title":"Frequency Reference","text":"

    Common frequencies for musical notes (A4 = 440 Hz):

    • C4: 261.63 Hz
    • D4: 293.66 Hz
    • E4: 329.63 Hz
    • F4: 349.23 Hz
    • G4: 392.00 Hz
    • A4: 440.00 Hz
    • B4: 493.88 Hz
    • C5: 523.25 Hz

    Example:

    // Play a C note\npixelroot32::audio::AudioEvent note{};\nnote.type = pixelroot32::audio::WaveType::PULSE;\nnote.frequency = 261.63f;  // C4\nnote.duration = 0.5f;\nnote.volume = 0.8f;\nnote.duty = 0.5f;\n

    "},{"location":"api_reference/audio/audio_types/#duty-cycle-pulse-wave","title":"Duty Cycle (Pulse Wave)","text":"

    Duty cycle controls the shape of the pulse wave:

    • 0.125 (12.5%): Thin pulse (NES-like)
    • 0.25 (25%): Narrow pulse
    • 0.5 (50%): Square wave (most common)
    • 0.75 (75%): Wide pulse

    Example:

    // Thin pulse (NES style)\nevent.duty = 0.125f;\n\n// Square wave (standard)\nevent.duty = 0.5f;\n

    "},{"location":"api_reference/audio/audio_types/#usage-examples","title":"Usage Examples","text":""},{"location":"api_reference/audio/audio_types/#creating-sound-effect-library","title":"Creating Sound Effect Library","text":"
    namespace SoundEffects {\n    // Jump sound\n    inline pixelroot32::audio::AudioEvent jump() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::PULSE;\n        evt.frequency = 800.0f;\n        evt.duration = 0.1f;\n        evt.volume = 0.7f;\n        evt.duty = 0.5f;\n        return evt;\n    }\n\n    // Hit sound\n    inline pixelroot32::audio::AudioEvent hit() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::NOISE;\n        evt.frequency = 500.0f;\n        evt.duration = 0.05f;\n        evt.volume = 0.5f;\n        return evt;\n    }\n\n    // Collect sound\n    inline pixelroot32::audio::AudioEvent collect() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::TRIANGLE;\n        evt.frequency = 1000.0f;\n        evt.duration = 0.15f;\n        evt.volume = 0.6f;\n        return evt;\n    }\n}\n\n// Usage\nauto& audio = engine.getAudioEngine();\naudio.playEvent(SoundEffects::jump());\naudio.playEvent(SoundEffects::hit());\n
    "},{"location":"api_reference/audio/audio_types/#frequency-sweep-effect","title":"Frequency Sweep Effect","text":"
    void playSweepSound() {\n    auto& audio = engine.getAudioEngine();\n\n    // Create multiple events for sweep effect\n    for (int i = 0; i < 5; i++) {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::PULSE;\n        evt.frequency = 400.0f + (i * 200.0f);  // Sweep from 400 to 1200 Hz\n        evt.duration = 0.05f;\n        evt.volume = 0.6f;\n        evt.duty = 0.5f;\n\n        audio.playEvent(evt);\n        delay(50);  // Small delay between events\n    }\n}\n
    "},{"location":"api_reference/audio/audio_types/#performance-considerations","title":"Performance Considerations","text":"
    • Event creation: Creating events is fast (just struct initialization)
    • Channel allocation: Events are queued and played when channels are available
    • Frequency range: Keep frequencies in reasonable range (100-5000 Hz) for best results
    • Duration: Shorter durations free channels faster
    "},{"location":"api_reference/audio/audio_types/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Events are small structs, safe to create frequently
    • CPU: Audio generation is efficient but limit simultaneous sounds
    • Quality: Lower sample rates reduce CPU usage
    "},{"location":"api_reference/audio/audio_types/#see-also","title":"See Also","text":"
    • AudioEngine - Audio playback engine
    • AudioConfig - Audio configuration
    • Manual - Audio
    • API Overview
    "},{"location":"api_reference/audio/music_player/","title":"MusicPlayer","text":"

    Lightweight sequencer built on top of AudioEngine to play background melodies as tracks.

    "},{"location":"api_reference/audio/music_player/#description","title":"Description","text":"

    MusicPlayer is a simple sequencer that plays MusicTrack structures. It advances notes based on game time, converts MusicNote entries to AudioEvent calls, and manages playback state (play, stop, pause, resume).

    The player uses one audio channel (typically a Pulse channel) for music, leaving other channels available for sound effects.

    "},{"location":"api_reference/audio/music_player/#namespace","title":"Namespace","text":"
    namespace pixelroot32::audio {\n    class MusicPlayer {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/audio/music_player/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Engine (manages music player instance)
    "},{"location":"api_reference/audio/music_player/#constructors","title":"Constructors","text":""},{"location":"api_reference/audio/music_player/#musicplayeraudioengine-engine","title":"MusicPlayer(AudioEngine& engine)","text":"

    Constructs the MusicPlayer.

    Parameters: - engine (AudioEngine&): Reference to the AudioEngine used to play sounds

    Notes: - Typically created and managed by Engine - Access via engine.getMusicPlayer()

    Example:

    auto& audio = engine.getAudioEngine();\npixelroot32::audio::MusicPlayer musicPlayer(audio);\n

    "},{"location":"api_reference/audio/music_player/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/audio/music_player/#void-playconst-musictrack-track","title":"void play(const MusicTrack& track)","text":"

    Starts playing a track.

    Parameters: - track (const MusicTrack&): The track to play

    Returns: - void

    Notes: - Stops any currently playing track - Starts from the beginning of the track - If track has loop = true, will loop automatically - Uses one audio channel (typically Pulse)

    Example:

    static const MusicNote MELODY[] = {\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::E, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::G, 0.25f),\n    makeRest(0.10f),\n};\n\nstatic const MusicTrack GAME_MUSIC = {\n    MELODY,\n    sizeof(MELODY) / sizeof(MusicNote),\n    true,  // loop\n    WaveType::PULSE,\n    0.5f   // volume\n};\n\nvoid init() override {\n    auto& music = engine.getMusicPlayer();\n    music.play(GAME_MUSIC);\n}\n

    "},{"location":"api_reference/audio/music_player/#void-stop","title":"void stop()","text":"

    Stops playback and silences the channel.

    Returns: - void

    Notes: - Immediately stops the current note - Resets playback to the beginning - Channel is freed for other use

    Example:

    void onGameOver() {\n    auto& music = engine.getMusicPlayer();\n    music.stop();\n}\n

    "},{"location":"api_reference/audio/music_player/#void-pause","title":"void pause()","text":"

    Pauses playback.

    Returns: - void

    Notes: - Current note continues until it ends, then playback pauses - Playback state is preserved (can resume from where it paused) - Use for pause menus

    Example:

    void onPause() {\n    auto& music = engine.getMusicPlayer();\n    music.pause();\n}\n

    "},{"location":"api_reference/audio/music_player/#void-resume","title":"void resume()","text":"

    Resumes playback.

    Returns: - void

    Notes: - Only works if playback was paused - Resumes from where it was paused - Use to unpause after pause menu

    Example:

    void onResume() {\n    auto& music = engine.getMusicPlayer();\n    music.resume();\n}\n

    "},{"location":"api_reference/audio/music_player/#void-updateunsigned-long-deltatime","title":"void update(unsigned long deltaTime)","text":"

    Updates the player state. Should be called every frame.

    Parameters: - deltaTime (unsigned long): Time elapsed since last frame in milliseconds

    Returns: - void

    Notes: - Must be called every frame for proper timing - Advances note playback based on elapsed time - Automatically plays next notes in sequence - Typically called automatically by Engine

    Example:

    // Called automatically by Engine, but can be called manually:\nvoid update(unsigned long deltaTime) override {\n    Scene::update(deltaTime);\n\n    // Music player is updated automatically by Engine\n    // No need to call manually\n}\n

    "},{"location":"api_reference/audio/music_player/#bool-isplaying-const","title":"bool isPlaying() const","text":"

    Checks if a track is currently playing.

    Returns: - bool: true if playing, false otherwise

    Notes: - Returns false if stopped or paused - Use to check playback state before operations

    Example:

    auto& music = engine.getMusicPlayer();\nif (music.isPlaying()) {\n    // Music is active\n} else {\n    // Music is stopped or paused\n}\n

    "},{"location":"api_reference/audio/music_player/#void-settempofactorfloat-factor","title":"void setTempoFactor(float factor)","text":"

    Sets the global tempo scaling factor.

    Parameters: - factor (float): Tempo multiplier - 1.0f: Normal speed - 2.0f: Double speed - 0.5f: Half speed

    Returns: - void

    Notes: - Affects all note durations - Useful for speed-up effects or slow-motion - Applied to all tracks

    Example:

    auto& music = engine.getMusicPlayer();\nmusic.setTempoFactor(1.5f);  // 50% faster\nmusic.setTempoFactor(0.5f);   // 50% slower\nmusic.setTempoFactor(1.0f);   // Normal speed\n

    "},{"location":"api_reference/audio/music_player/#float-gettempofactor-const","title":"float getTempoFactor() const","text":"

    Gets the current tempo scaling factor.

    Returns: - float: Current factor (default 1.0f)

    Example:

    float currentTempo = musicPlayer.getTempoFactor();\n

    "},{"location":"api_reference/audio/music_player/#musictrack-structure","title":"MusicTrack Structure","text":"

    A MusicTrack contains:

    • notes (const MusicNote*): Array of music notes
    • noteCount (size_t): Number of notes in the array
    • loop (bool): Whether to loop the track
    • waveType (WaveType): Wave type to use (typically PULSE)
    • volume (float): Volume level (0.0 to 1.0)
    "},{"location":"api_reference/audio/music_player/#musicnote-structure","title":"MusicNote Structure","text":"

    A MusicNote contains:

    • instrument (InstrumentPreset): Instrument preset to use
    • note (Note): Musical note (C, D, E, etc.)
    • duration (float): Duration in seconds

    Use helper functions: - makeNote(instrument, note, duration): Create a note - makeRest(duration): Create a rest (silence)

    "},{"location":"api_reference/audio/music_player/#usage-example","title":"Usage Example","text":"
    #include \"audio/MusicPlayer.h\"\n#include \"audio/AudioMusicTypes.h\"\n\nusing namespace pixelroot32::audio;\n\n// Define a simple melody\nstatic const MusicNote MAIN_THEME[] = {\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.25f),\n    makeNote(INSTR_PULSE_LEAD, Note::E, 0.25f),\n    makeNote(INSTR_PULSE_LEAD, Note::G, 0.25f),\n    makeRest(0.1f),\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.5f),\n    makeRest(0.2f),\n};\n\nstatic const MusicTrack MAIN_THEME_TRACK = {\n    MAIN_THEME,\n    sizeof(MAIN_THEME) / sizeof(MusicNote),\n    true,           // loop\n    WaveType::PULSE,\n    0.6f            // volume\n};\n\nclass GameScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Start background music\n        auto& music = engine.getMusicPlayer();\n        music.play(MAIN_THEME_TRACK);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Music updates automatically\n    }\n\n    void onPauseMenu() {\n        auto& music = engine.getMusicPlayer();\n        music.pause();\n    }\n\n    void onResumeGame() {\n        auto& music = engine.getMusicPlayer();\n        music.resume();\n    }\n\n    void onGameOver() {\n        auto& music = engine.getMusicPlayer();\n        music.stop();\n    }\n};\n
    "},{"location":"api_reference/audio/music_player/#performance-considerations","title":"Performance Considerations","text":"
    • One channel: Music uses one channel, leaving others for sound effects
    • Update frequency: update() must be called every frame
    • Track size: Larger tracks use more memory (store in flash)
    • Tempo factor: Changing tempo is fast (just a multiplier)
    "},{"location":"api_reference/audio/music_player/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Store tracks in flash (const/constexpr) to save RAM
    • CPU: Music playback is lightweight (simple sequencing)
    • Channel conflict: Music and sound effects share channels; plan accordingly
    "},{"location":"api_reference/audio/music_player/#see-also","title":"See Also","text":"
    • AudioEngine - Audio playback engine
    • AudioTypes - Audio data structures
    • AudioMusicTypes - Music data structures
    • Manual - Audio
    • API Overview
    "},{"location":"api_reference/core/actor/","title":"Actor","text":"

    An Entity capable of physical interaction and collision.

    "},{"location":"api_reference/core/actor/#description","title":"Description","text":"

    Actor extends Entity with collision layers and masks. Actors are used for dynamic game objects like players, enemies, projectiles, and obstacles that need to interact with each other through collision detection.

    Actors participate in the collision system and can detect collisions with other actors based on their collision layers and masks.

    "},{"location":"api_reference/core/actor/#namespace","title":"Namespace","text":"
    namespace pixelroot32::core {\n    class Actor : public Entity {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/actor/#inheritance","title":"Inheritance","text":"
    • Inherits from: Entity
    • Inherited by: PhysicsActor and your custom actor classes
    "},{"location":"api_reference/core/actor/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/actor/#actorfloat-x-float-y-int-w-int-h","title":"Actor(float x, float y, int w, int h)","text":"

    Creates a new actor with specified position and size.

    Parameters: - x (float): Initial X position in world space - y (float): Initial Y position in world space - w (int): Actor width in pixels - h (int): Actor height in pixels

    Notes: - Actor type is automatically set to EntityType::ACTOR - Collision layer and mask default to DefaultLayers::kNone - Must set collision layer and mask for collision detection to work

    Example:

    class PlayerActor : public pixelroot32::core::Actor {\npublic:\n    PlayerActor(float x, float y) \n        : Actor(x, y, 16, 16) {\n        // Set collision layer and mask\n        layer = pixelroot32::physics::DefaultLayers::kPlayer;\n        mask = pixelroot32::physics::DefaultLayers::kEnemy | \n               pixelroot32::physics::DefaultLayers::kObstacle;\n    }\n\n    void update(unsigned long deltaTime) override {\n        Actor::update(deltaTime);\n        // Player logic\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawSprite(playerSprite, \n                           static_cast<int>(x), \n                           static_cast<int>(y), \n                           Color::White);\n    }\n\n    Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(Actor* other) override {\n        // Handle collision\n    }\n};\n

    "},{"location":"api_reference/core/actor/#public-properties","title":"Public Properties","text":""},{"location":"api_reference/core/actor/#collisionlayer-layer","title":"CollisionLayer layer","text":"

    The collision layer this actor belongs to.

    Type: pixelroot32::physics::CollisionLayer (uint16_t)

    Access: Read-write

    Default: DefaultLayers::kNone

    Notes: - Defines which layer this actor is on - Use bit flags to assign multiple layers (e.g., kPlayer | kProjectile) - Only actors with matching layers in their mask will collide

    Example:

    actor->layer = pixelroot32::physics::DefaultLayers::kPlayer;\n

    "},{"location":"api_reference/core/actor/#collisionlayer-mask","title":"CollisionLayer mask","text":"

    The collision layers this actor interacts with.

    Type: pixelroot32::physics::CollisionLayer (uint16_t)

    Access: Read-write

    Default: DefaultLayers::kNone

    Notes: - Defines which layers this actor can collide with - Use bit flags to check multiple layers (e.g., kEnemy | kObstacle) - Collision only occurs if the other actor's layer matches bits in this mask

    Example:

    // Actor collides with enemies and obstacles\nactor->mask = pixelroot32::physics::DefaultLayers::kEnemy | \n              pixelroot32::physics::DefaultLayers::kObstacle;\n

    "},{"location":"api_reference/core/actor/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/actor/#void-setcollisionlayercollisionlayer-l","title":"void setCollisionLayer(CollisionLayer l)","text":"

    Sets the collision layer for this actor.

    Parameters: - l (pixelroot32::physics::CollisionLayer): The layer to set

    Returns: - void

    Notes: - Equivalent to setting layer directly - Use bit flags for multiple layers

    Example:

    actor->setCollisionLayer(pixelroot32::physics::DefaultLayers::kPlayer);\n

    "},{"location":"api_reference/core/actor/#void-setcollisionmaskcollisionlayer-m","title":"void setCollisionMask(CollisionLayer m)","text":"

    Sets the collision mask for this actor.

    Parameters: - m (pixelroot32::physics::CollisionLayer): The mask to set

    Returns: - void

    Notes: - Equivalent to setting mask directly - Use bit flags for multiple layers

    Example:

    actor->setCollisionMask(pixelroot32::physics::DefaultLayers::kEnemy | \n                        pixelroot32::physics::DefaultLayers::kObstacle);\n

    "},{"location":"api_reference/core/actor/#bool-isinlayeruint16_t-targetlayer-const","title":"bool isInLayer(uint16_t targetLayer) const","text":"

    Checks if the Actor belongs to a specific collision layer.

    Parameters: - targetLayer (uint16_t): The bit(s) to check (e.g., DefaultLayers::kPlayer)

    Returns: - bool: true if the bit is set in the actor's layer

    Notes: - Uses bitwise AND operation - Useful for checking if an actor is on a specific layer

    Example:

    if (actor->isInLayer(pixelroot32::physics::DefaultLayers::kPlayer)) {\n    // This is a player actor\n}\n

    "},{"location":"api_reference/core/actor/#virtual-rect-gethitbox-0","title":"virtual Rect getHitBox() = 0","text":"

    Gets the hitbox for collision detection. Must be implemented by derived classes.

    Returns: - Rect: A rectangle representing the collision bounds

    Notes: - Called by the collision system to check collisions - Should return the actual collision bounds (may differ from visual size) - Use AABB (Axis-Aligned Bounding Box) for efficiency

    Example:

    Rect getHitBox() override {\n    // Return collision bounds (may be smaller than visual)\n    return {x + 2, y + 2, width - 4, height - 4};\n}\n

    "},{"location":"api_reference/core/actor/#virtual-void-oncollisionactor-other-0","title":"virtual void onCollision(Actor* other) = 0","text":"

    Callback invoked when a collision occurs. Must be implemented by derived classes.

    Parameters: - other (Actor*): The actor that this actor collided with

    Notes: - Called automatically by the collision system when a collision is detected - Both actors' onCollision() methods are called - Use to handle collision responses (damage, bouncing, etc.)

    Example:

    void onCollision(Actor* other) override {\n    // Check what we collided with\n    if (other->isInLayer(pixelroot32::physics::DefaultLayers::kEnemy)) {\n        // Take damage\n        health--;\n        if (health <= 0) {\n            isEnabled = false;\n        }\n    } else if (other->isInLayer(pixelroot32::physics::DefaultLayers::kCollectible)) {\n        // Collect item\n        score += 10;\n        other->isEnabled = false;  // Remove collectible\n    }\n}\n

    "},{"location":"api_reference/core/actor/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the actor logic. Default implementation does nothing.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    Notes: - Override to implement actor-specific update logic - Called automatically by Scene if isEnabled is true - Use deltaTime for frame-rate independent movement

    Example:

    void update(unsigned long deltaTime) override {\n    Actor::update(deltaTime);  // Call base implementation\n\n    // Move actor\n    float speed = 100.0f;  // pixels per second\n    x += (speed * deltaTime) / 1000.0f;\n}\n

    "},{"location":"api_reference/core/actor/#collision-layers","title":"Collision Layers","text":"

    Collision layers use bit flags to organize actors into groups. Common layers:

    • DefaultLayers::kNone (0): No layer
    • DefaultLayers::kPlayer (1 << 0): Player actors
    • DefaultLayers::kEnemy (1 << 1): Enemy actors
    • DefaultLayers::kObstacle (1 << 2): Obstacles/walls
    • DefaultLayers::kProjectile (1 << 3): Projectiles
    • DefaultLayers::kCollectible (1 << 4): Collectible items

    Example:

    // Player collides with enemies and obstacles\nplayer->layer = DefaultLayers::kPlayer;\nplayer->mask = DefaultLayers::kEnemy | DefaultLayers::kObstacle;\n\n// Enemy collides with player and obstacles\nenemy->layer = DefaultLayers::kEnemy;\nenemy->mask = DefaultLayers::kPlayer | DefaultLayers::kObstacle;\n\n// Projectile collides with enemies\nprojectile->layer = DefaultLayers::kProjectile;\nprojectile->mask = DefaultLayers::kEnemy;\n

    "},{"location":"api_reference/core/actor/#usage-example","title":"Usage Example","text":"
    #include \"core/Actor.h\"\n#include \"physics/CollisionTypes.h\"\n\nclass EnemyActor : public pixelroot32::core::Actor {\nprivate:\n    const pixelroot32::graphics::Sprite* sprite;\n    int health = 3;\n\npublic:\n    EnemyActor(float x, float y) \n        : Actor(x, y, 16, 16),\n          sprite(&enemySprite) {\n        // Set collision layer and mask\n        layer = pixelroot32::physics::DefaultLayers::kEnemy;\n        mask = pixelroot32::physics::DefaultLayers::kPlayer | \n               pixelroot32::physics::DefaultLayers::kProjectile;\n    }\n\n    void update(unsigned long deltaTime) override {\n        Actor::update(deltaTime);\n\n        // Move towards player\n        float speed = 50.0f;\n        // ... movement logic\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawSprite(*sprite, \n                           static_cast<int>(x), \n                           static_cast<int>(y), \n                           Color::Red);\n    }\n\n    Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(Actor* other) override {\n        if (other->isInLayer(pixelroot32::physics::DefaultLayers::kProjectile)) {\n            // Hit by projectile\n            health--;\n            if (health <= 0) {\n                isEnabled = false;  // Remove enemy\n            }\n        }\n    }\n};\n
    "},{"location":"api_reference/core/actor/#performance-considerations","title":"Performance Considerations","text":"
    • Collision layers: Use layers efficiently to reduce collision checks
    • Hitbox size: Keep hitboxes simple (AABB) for best performance
    • Collision callbacks: Keep onCollision() fast; avoid expensive operations
    • Layer organization: Group actors by layer to minimize checks
    "},{"location":"api_reference/core/actor/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Collision checks: Collision system automatically optimizes using layers
    • Memory: Each actor consumes memory; stay within MAX_ENTITIES limit
    • Object pooling: Reuse actors instead of creating/destroying frequently
    "},{"location":"api_reference/core/actor/#see-also","title":"See Also","text":"
    • Entity - Base entity class
    • PhysicsActor - Entity with physics
    • CollisionSystem - Collision detection
    • CollisionTypes - Collision layer definitions
    • Manual - Scenes and Entities
    • Manual - Physics and Collisions
    • API Overview
    "},{"location":"api_reference/core/engine/","title":"Engine","text":"

    The main engine class that manages the game loop and core subsystems.

    "},{"location":"api_reference/core/engine/#description","title":"Description","text":"

    Engine acts as the central hub of the PixelRoot32 game engine. It initializes and manages the Renderer, InputManager, AudioEngine, and SceneManager. It runs the main game loop, handling timing (delta time), updating the current scene, and rendering frames.

    The engine provides a unified interface for both ESP32 and Native (SDL2) platforms, abstracting platform-specific details while maintaining consistent behavior.

    "},{"location":"api_reference/core/engine/#namespace","title":"Namespace","text":"
    namespace pixelroot32::core {\n    class Engine {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/engine/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Application entry point (main.cpp)
    "},{"location":"api_reference/core/engine/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/engine/#engineconst-displayconfig-displayconfig-const-inputconfig-inputconfig-const-audioconfig-audioconfig","title":"Engine(const DisplayConfig& displayConfig, const InputConfig& inputConfig, const AudioConfig& audioConfig)","text":"

    Creates a new engine instance with custom display, input, and audio configurations.

    Parameters: - displayConfig (const pixelroot32::graphics::DisplayConfig&): Configuration settings for the display (width, height, rotation, etc.) - inputConfig (const pixelroot32::input::InputConfig&): Configuration settings for the input system (pins, buttons) - audioConfig (const pixelroot32::audio::AudioConfig&): Configuration settings for the audio system (backend, sample rate, buffer size)

    Example:

    #include \"core/Engine.h\"\n#include \"graphics/DisplayConfig.h\"\n#include \"input/InputConfig.h\"\n#include \"audio/AudioConfig.h\"\n\npixelroot32::graphics::DisplayConfig displayConfig;\ndisplayConfig.width = 128;\ndisplayConfig.height = 128;\n\npixelroot32::input::InputConfig inputConfig;\n// Configure input pins...\n\npixelroot32::audio::AudioConfig audioConfig;\naudioConfig.backend = pixelroot32::audio::AudioConfig::Backend::ESP32_DAC;\naudioConfig.sampleRate = 11025;\n\npixelroot32::core::Engine engine(displayConfig, inputConfig, audioConfig);\nengine.init();\nengine.run();\n

    "},{"location":"api_reference/core/engine/#engineconst-displayconfig-displayconfig-const-inputconfig-inputconfig","title":"Engine(const DisplayConfig& displayConfig, const InputConfig& inputConfig)","text":"

    Creates a new engine instance with custom display and input configurations, using default audio settings.

    Parameters: - displayConfig (const pixelroot32::graphics::DisplayConfig&): Configuration settings for the display - inputConfig (const pixelroot32::input::InputConfig&): Configuration settings for the input system

    Example:

    pixelroot32::core::Engine engine(displayConfig, inputConfig);\nengine.init();\nengine.run();\n

    "},{"location":"api_reference/core/engine/#engineconst-displayconfig-displayconfig","title":"Engine(const DisplayConfig& displayConfig)","text":"

    Creates a new engine instance with custom display configuration and default input/audio settings.

    Parameters: - displayConfig (const pixelroot32::graphics::DisplayConfig&): Configuration settings for the display

    Example:

    pixelroot32::core::Engine engine(displayConfig);\nengine.init();\nengine.run();\n

    "},{"location":"api_reference/core/engine/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/engine/#void-init","title":"void init()","text":"

    Initializes the engine subsystems. This method must be called before run().

    Returns: - void

    Notes: - Initializes the Renderer, InputManager, and sets up the initial state - Must be called after construction and before run() - Safe to call multiple times (idempotent)

    Example:

    Engine engine(displayConfig);\nengine.init();  // Initialize subsystems\nengine.setScene(myScene);\nengine.run();   // Start game loop\n

    "},{"location":"api_reference/core/engine/#void-run","title":"void run()","text":"

    Starts the main game loop. This method contains the infinite loop that calls update() and draw() repeatedly until the application exits.

    Returns: - void

    Notes: - This method blocks until the application exits - Handles frame timing and delta time calculation automatically - Calls update() and draw() once per frame - Do not call this method multiple times

    Example:

    Engine engine(displayConfig);\nengine.init();\nengine.setScene(myScene);\nengine.run();  // Blocks here, runs game loop\n

    "},{"location":"api_reference/core/engine/#unsigned-long-getdeltatime-const","title":"unsigned long getDeltaTime() const","text":"

    Gets the time elapsed since the last frame.

    Returns: - unsigned long: The delta time in milliseconds

    Performance Notes: - Very fast (inline accessor) - Safe to call every frame - Use this value to make movement frame-rate independent

    Example:

    void update(unsigned long deltaTime) override {\n    auto& engine = getEngine();\n    unsigned long dt = engine.getDeltaTime();\n\n    // Move at constant speed regardless of FPS\n    float speed = 100.0f;  // pixels per second\n    x += (speed * dt) / 1000.0f;\n}\n

    "},{"location":"api_reference/core/engine/#void-setscenescene-newscene","title":"void setScene(Scene* newScene)","text":"

    Sets the current active scene to be updated and rendered.

    Parameters: - newScene (Scene*): Pointer to the new Scene to become active. Can be nullptr to clear the current scene.

    Notes: - The previous scene is replaced (not pushed onto a stack) - Use SceneManager for push/pop operations if needed - The scene's init() method will be called automatically - Safe to call during the game loop

    Example:

    class MainMenuScene : public pixelroot32::core::Scene {\n    // ...\n};\n\nclass GameScene : public pixelroot32::core::Scene {\n    // ...\n};\n\nMainMenuScene menuScene;\nGameScene gameScene;\n\nEngine engine(displayConfig);\nengine.init();\nengine.setScene(&menuScene);  // Start with menu\nengine.run();\n

    "},{"location":"api_reference/core/engine/#scene-getcurrentscene-const","title":"Scene* getCurrentScene() const","text":"

    Retrieves the currently active scene.

    Returns: - Scene*: Pointer to the current Scene, or nullptr if none is set

    Example:

    auto* currentScene = engine.getCurrentScene();\nif (currentScene) {\n    // Scene is active\n}\n

    "},{"location":"api_reference/core/engine/#void-setrendererrenderer-newrenderer","title":"void setRenderer(Renderer& newRenderer)","text":"

    Replaces the current renderer instance.

    Parameters: - newRenderer (pixelroot32::graphics::Renderer&): Reference to the new Renderer to use

    Notes: - Advanced usage: typically not needed unless implementing custom renderer - The renderer must be properly initialized before use - Use with caution: may break existing rendering code

    "},{"location":"api_reference/core/engine/#renderer-getrenderer","title":"Renderer& getRenderer()","text":"

    Provides access to the Renderer subsystem.

    Returns: - pixelroot32::graphics::Renderer&: Reference to the current Renderer

    Example:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    auto& engineRenderer = engine.getRenderer();\n    engineRenderer.drawSprite(mySprite, 100, 100, Color::White);\n}\n

    "},{"location":"api_reference/core/engine/#inputmanager-getinputmanager","title":"InputManager& getInputManager()","text":"

    Provides access to the InputManager subsystem.

    Returns: - pixelroot32::input::InputManager&: Reference to the InputManager

    Example:

    void update(unsigned long deltaTime) override {\n    auto& input = engine.getInputManager();\n    if (input.isButtonPressed(Buttons::A)) {\n        // Handle button press\n    }\n}\n

    "},{"location":"api_reference/core/engine/#audioengine-getaudioengine","title":"AudioEngine& getAudioEngine()","text":"

    Provides access to the AudioEngine subsystem.

    Returns: - pixelroot32::audio::AudioEngine&: Reference to the AudioEngine

    Example:

    void playSound() {\n    auto& audio = engine.getAudioEngine();\n    pixelroot32::audio::AudioEvent sound{};\n    sound.type = pixelroot32::audio::WaveType::PULSE;\n    sound.frequency = 800.0f;\n    sound.duration = 0.1f;\n    audio.playEvent(sound);\n}\n

    "},{"location":"api_reference/core/engine/#musicplayer-getmusicplayer","title":"MusicPlayer& getMusicPlayer()","text":"

    Provides access to the MusicPlayer subsystem.

    Returns: - pixelroot32::audio::MusicPlayer&: Reference to the MusicPlayer

    Example:

    void playMusic() {\n    auto& music = engine.getMusicPlayer();\n    music.playTrack(myMusicTrack);\n}\n

    "},{"location":"api_reference/core/engine/#usage-example","title":"Usage Example","text":"
    #include \"core/Engine.h\"\n#include \"graphics/DisplayConfig.h\"\n#include \"MyScene.h\"\n\nvoid setup() {\n    // Configure display\n    pixelroot32::graphics::DisplayConfig displayConfig;\n    displayConfig.width = 128;\n    displayConfig.height = 128;\n    displayConfig.rotation = 0;\n\n    // Create engine\n    pixelroot32::core::Engine engine(displayConfig);\n\n    // Initialize\n    engine.init();\n\n    // Create and set scene\n    MyScene myScene;\n    engine.setScene(&myScene);\n\n    // Run game loop\n    engine.run();\n}\n
    "},{"location":"api_reference/core/engine/#performance-considerations","title":"Performance Considerations","text":"
    • Initialization: init() should be called once at startup, not in the game loop
    • Scene switching: Switching scenes is fast but avoid doing it every frame
    • Subsystem access: Getters are inline and very fast; safe to call every frame
    • Delta time: Use getDeltaTime() for frame-rate independent movement
    "},{"location":"api_reference/core/engine/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Ensure init() completes before run() to avoid initialization issues
    • Monitor memory usage when switching scenes frequently
    • Use getDeltaTime() for consistent gameplay across different frame rates
    "},{"location":"api_reference/core/engine/#see-also","title":"See Also","text":"
    • Scene - Scene management
    • Renderer - Rendering system
    • InputManager - Input handling
    • AudioEngine - Audio system
    • Getting Started - Fundamental Concepts
    • Manual - Scenes and Entities
    • API Overview
    "},{"location":"api_reference/core/entity/","title":"Entity","text":"

    Abstract base class for all game objects.

    "},{"location":"api_reference/core/entity/#description","title":"Description","text":"

    Entity is the fundamental building block of the scene. Entities have a position, size, and lifecycle methods (update, draw). All game objects inherit from Entity, including actors, UI elements, and custom game objects.

    Entities are managed by Scene and are automatically updated and drawn each frame when enabled and visible.

    "},{"location":"api_reference/core/entity/#namespace","title":"Namespace","text":"
    namespace pixelroot32::core {\n    class Entity {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/entity/#inheritance","title":"Inheritance","text":"
    • Base class: None (abstract base class)
    • Inherited by: Actor, UI elements, and your custom entity classes
    "},{"location":"api_reference/core/entity/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/entity/#entityfloat-x-float-y-int-w-int-h-entitytype-t","title":"Entity(float x, float y, int w, int h, EntityType t)","text":"

    Creates a new entity with specified position, size, and type.

    Parameters: - x (float): Initial X position in world space - y (float): Initial Y position in world space - w (int): Width in pixels - h (int): Height in pixels - t (EntityType): The type of entity (GENERIC, ACTOR, UI_ELEMENT)

    Example:

    class MyEntity : public pixelroot32::core::Entity {\npublic:\n    MyEntity(float x, float y) \n        : Entity(x, y, 16, 16, EntityType::GENERIC) {}\n\n    void update(unsigned long deltaTime) override {\n        // Update logic\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw logic\n    }\n};\n

    "},{"location":"api_reference/core/entity/#public-properties","title":"Public Properties","text":""},{"location":"api_reference/core/entity/#float-x-y","title":"float x, y","text":"

    Position of the entity in world space.

    Type: float

    Access: Read-write

    Notes: - Position is in world coordinates, not screen coordinates - Can be modified directly or through helper methods - Use for entity positioning and movement

    Example:

    entity->x = 100.0f;\nentity->y = 50.0f;\n

    "},{"location":"api_reference/core/entity/#int-width-height","title":"int width, height","text":"

    Dimensions of the entity in pixels.

    Type: int

    Access: Read-write

    Notes: - Used for collision detection, rendering bounds, and layout - Should match the visual size of the entity - Can be modified at runtime if needed

    Example:

    entity->width = 32;\nentity->height = 32;\n

    "},{"location":"api_reference/core/entity/#entitytype-type","title":"EntityType type","text":"

    The specific type of this entity.

    Type: EntityType enum

    Access: Read-only (set in constructor)

    Values: - EntityType::GENERIC: Generic entity - EntityType::ACTOR: Actor entity (with collision) - EntityType::UI_ELEMENT: UI element

    Notes: - Used for type-safe casting and logic differentiation - Set once in constructor, typically not changed

    "},{"location":"api_reference/core/entity/#bool-isvisible","title":"bool isVisible","text":"

    If false, the entity's draw() method will not be called.

    Type: bool

    Access: Read-write

    Default: true

    Notes: - Use to hide entities without removing them from the scene - More efficient than removing and re-adding entities - Useful for object pooling

    Example:

    entity->isVisible = false;  // Hide entity\nentity->setVisible(true);   // Show entity\n

    "},{"location":"api_reference/core/entity/#bool-isenabled","title":"bool isEnabled","text":"

    If false, the entity's update() method will not be called.

    Type: bool

    Access: Read-write

    Default: true

    Notes: - Use to disable entity logic without removing it - Entity still exists but doesn't update - Useful for paused entities or object pooling

    Example:

    entity->isEnabled = false;  // Disable updates\nentity->setEnabled(true);   // Enable updates\n

    "},{"location":"api_reference/core/entity/#unsigned-char-renderlayer","title":"unsigned char renderLayer","text":"

    The render layer this entity is drawn on.

    Type: unsigned char

    Access: Read-write

    Default: 1

    Notes: - Layers are drawn in ascending order (0 = background, 1 = gameplay, 2 = UI) - Entities on the same layer are drawn in add order - Use to control draw order without changing entity order

    Example:

    entity->renderLayer = 0;  // Background layer\nentity->setRenderLayer(2); // UI layer\n

    "},{"location":"api_reference/core/entity/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/entity/#virtual-void-setvisiblebool-v","title":"virtual void setVisible(bool v)","text":"

    Sets the visibility of the entity.

    Parameters: - v (bool): true to show, false to hide

    Returns: - void

    Notes: - Equivalent to setting isVisible directly - Can be overridden for custom visibility logic

    Example:

    entity->setVisible(false);  // Hide\nentity->setVisible(true);   // Show\n

    "},{"location":"api_reference/core/entity/#virtual-void-setenabledbool-e","title":"virtual void setEnabled(bool e)","text":"

    Sets the enabled state of the entity.

    Parameters: - e (bool): true to enable, false to disable

    Returns: - void

    Notes: - Equivalent to setting isEnabled directly - Can be overridden for custom enable logic

    Example:

    entity->setEnabled(false);  // Disable updates\nentity->setEnabled(true);   // Enable updates\n

    "},{"location":"api_reference/core/entity/#unsigned-char-getrenderlayer-const","title":"unsigned char getRenderLayer() const","text":"

    Gets the current render layer.

    Returns: - unsigned char: The render layer (0-255)

    Example:

    unsigned char layer = entity->getRenderLayer();\n

    "},{"location":"api_reference/core/entity/#virtual-void-setrenderlayerunsigned-char-layer","title":"virtual void setRenderLayer(unsigned char layer)","text":"

    Sets the render layer for this entity.

    Parameters: - layer (unsigned char): The render layer (0 = background, 1 = gameplay, 2 = UI)

    Returns: - void

    Example:

    entity->setRenderLayer(0);  // Background\n

    "},{"location":"api_reference/core/entity/#virtual-void-updateunsigned-long-deltatime-0","title":"virtual void update(unsigned long deltaTime) = 0","text":"

    Updates the entity's logic. Must be implemented by derived classes.

    Parameters: - deltaTime (unsigned long): Time elapsed since the last frame in milliseconds

    Returns: - void

    Notes: - Called automatically by Scene every frame if isEnabled is true - Use deltaTime for frame-rate independent movement - Override to implement entity-specific update logic

    Example:

    void update(unsigned long deltaTime) override {\n    // Move entity\n    float speed = 50.0f;  // pixels per second\n    x += (speed * deltaTime) / 1000.0f;\n\n    // Wrap around screen\n    if (x > 128) {\n        x = 0;\n    }\n}\n

    "},{"location":"api_reference/core/entity/#virtual-void-drawrenderer-renderer-0","title":"virtual void draw(Renderer& renderer) = 0","text":"

    Renders the entity. Must be implemented by derived classes.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer to use for drawing

    Returns: - void

    Notes: - Called automatically by Scene every frame if isVisible is true - Entities are drawn in render layer order, then in add order - Override to implement entity-specific drawing logic

    Example:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Draw sprite at entity position\n    renderer.drawSprite(mySprite, static_cast<int>(x), static_cast<int>(y), Color::White);\n}\n

    "},{"location":"api_reference/core/entity/#entitytype-enum","title":"EntityType Enum","text":"

    Categorizes entities for type-safe casting and logic differentiation.

    Values: - EntityType::GENERIC: Generic entity (default) - EntityType::ACTOR: Actor entity (with collision support) - EntityType::UI_ELEMENT: UI element

    Example:

    if (entity->type == EntityType::ACTOR) {\n    Actor* actor = static_cast<Actor*>(entity);\n    // Use actor-specific methods\n}\n

    "},{"location":"api_reference/core/entity/#rect-structure","title":"Rect Structure","text":"

    Represents a 2D rectangle, typically used for hitboxes or bounds.

    Members: - float x, y: Top-left corner coordinates - int width, height: Dimensions of the rectangle

    Methods: - bool intersects(const Rect& other): Checks if this rectangle intersects with another

    Example:

    pixelroot32::core::Rect rect1{10.0f, 20.0f, 50, 50};\npixelroot32::core::Rect rect2{30.0f, 40.0f, 50, 50};\n\nif (rect1.intersects(rect2)) {\n    // Rectangles overlap\n}\n

    "},{"location":"api_reference/core/entity/#usage-example","title":"Usage Example","text":"
    #include \"core/Entity.h\"\n\nclass Collectible : public pixelroot32::core::Entity {\nprivate:\n    const pixelroot32::graphics::Sprite* sprite;\n\npublic:\n    Collectible(float x, float y) \n        : Entity(x, y, 8, 8, EntityType::GENERIC),\n          sprite(&collectibleSprite) {}\n\n    void update(unsigned long deltaTime) override {\n        // Rotate or animate\n        rotation += deltaTime * 0.001f;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        if (isVisible) {\n            renderer.drawSprite(*sprite, \n                               static_cast<int>(x), \n                               static_cast<int>(y), \n                               Color::Yellow);\n        }\n    }\n\nprivate:\n    float rotation = 0.0f;\n};\n
    "},{"location":"api_reference/core/entity/#performance-considerations","title":"Performance Considerations","text":"
    • Visibility: Use isVisible = false instead of removing entities when hiding
    • Enable state: Use isEnabled = false to pause entity logic
    • Render layers: Organize entities by layer to minimize layer switches
    • Direct access: Direct property access is fast (no function call overhead)
    "},{"location":"api_reference/core/entity/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Each entity consumes memory; stay within MAX_ENTITIES limit
    • Object pooling: Reuse entities instead of creating/destroying frequently
    • Update frequency: Disable entities that don't need to update every frame
    "},{"location":"api_reference/core/entity/#see-also","title":"See Also","text":"
    • Scene - Scene management
    • Actor - Entity with collision support
    • PhysicsActor - Entity with physics
    • Manual - Scenes and Entities
    • API Overview
    "},{"location":"api_reference/core/input_config/","title":"InputConfig","text":"

    Configuration structure for the InputManager.

    "},{"location":"api_reference/core/input_config/#description","title":"Description","text":"

    InputConfig defines the mapping between logical inputs and physical pins (ESP32) or keyboard keys (Native/SDL2). It uses variadic arguments to allow flexible configuration of any number of inputs.

    The configuration is platform-specific: ESP32 uses GPIO pin numbers, while Native uses SDL keyboard scancodes.

    "},{"location":"api_reference/core/input_config/#namespace","title":"Namespace","text":"
    namespace pixelroot32::input {\n    struct InputConfig {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/input_config/#structure","title":"Structure","text":""},{"location":"api_reference/core/input_config/#int-count","title":"int count","text":"

    Total number of configured inputs.

    Type: int

    Access: Read-write

    Default: 0

    Notes: - Must match the number of arguments provided to constructor - Determines the size of the internal button array

    "},{"location":"api_reference/core/input_config/#int-inputpins-esp32-only","title":"int* inputPins (ESP32 only)","text":"

    Array of GPIO pin numbers for ESP32.

    Type: int*

    Access: Read-write

    Default: nullptr

    Notes: - Only available on ESP32 platform - Array size equals count - Pin numbers correspond to ESP32 GPIO pins - Use nullptr if count is 0

    Example:

    // ESP32: 6 buttons on pins 0, 2, 4, 5, 18, 19\npixelroot32::input::InputConfig config(6, 0, 2, 4, 5, 18, 19);\n// config.inputPins[0] = 0  (Up)\n// config.inputPins[1] = 2  (Down)\n// config.inputPins[2] = 4  (Left)\n// config.inputPins[3] = 5  (Right)\n// config.inputPins[4] = 18 (Button A)\n// config.inputPins[5] = 19 (Button B)\n

    "},{"location":"api_reference/core/input_config/#uint8_t-buttonnames-native-only","title":"uint8_t* buttonNames (Native only)","text":"

    Array of button mappings (scancodes) for Native.

    Type: uint8_t*

    Access: Read-write

    Default: nullptr

    Notes: - Only available on Native platform - Array size equals count - Values are SDL keyboard scancodes - Use nullptr if count is 0

    Example:

    // Native: Map to keyboard keys\n#include <SDL2/SDL.h>\n\npixelroot32::input::InputConfig config(6,\n    SDL_SCANCODE_UP,    // Index 0\n    SDL_SCANCODE_DOWN,  // Index 1\n    SDL_SCANCODE_LEFT,  // Index 2\n    SDL_SCANCODE_RIGHT, // Index 3\n    SDL_SCANCODE_X,     // Index 4 (Button A)\n    SDL_SCANCODE_Z      // Index 5 (Button B)\n);\n

    "},{"location":"api_reference/core/input_config/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/input_config/#inputconfigint-count","title":"InputConfig(int count, ...)","text":"

    Constructs a new InputConfig with variadic arguments.

    Parameters: - count (int): Number of inputs to configure - ... (variadic): Variable arguments list of pins (ESP32) or scancodes (Native)

    Notes: - If count <= 0, configuration is empty (nullptr arrays) - Allocates arrays dynamically based on count - Arguments must match count in number - Platform-specific: ESP32 expects int (GPIO pins), Native expects int (SDL scancodes)

    ESP32 Example:

    // Configure 4 directional buttons\npixelroot32::input::InputConfig config(4, 0, 2, 4, 5);\n// Pin 0 = Up, Pin 2 = Down, Pin 4 = Left, Pin 5 = Right\n\n// Configure 6 buttons (4 directions + 2 action buttons)\npixelroot32::input::InputConfig config(6, 0, 2, 4, 5, 18, 19);\n

    Native Example:

    #include <SDL2/SDL.h>\n\n// Configure 4 directional buttons\npixelroot32::input::InputConfig config(4,\n    SDL_SCANCODE_UP,\n    SDL_SCANCODE_DOWN,\n    SDL_SCANCODE_LEFT,\n    SDL_SCANCODE_RIGHT\n);\n\n// Configure 6 buttons (4 directions + 2 action buttons)\npixelroot32::input::InputConfig config(6,\n    SDL_SCANCODE_UP,\n    SDL_SCANCODE_DOWN,\n    SDL_SCANCODE_LEFT,\n    SDL_SCANCODE_RIGHT,\n    SDL_SCANCODE_X,  // Button A\n    SDL_SCANCODE_Z   // Button B\n);\n

    "},{"location":"api_reference/core/input_config/#usage-example","title":"Usage Example","text":""},{"location":"api_reference/core/input_config/#esp32-configuration","title":"ESP32 Configuration","text":"
    #include \"input/InputConfig.h\"\n#include \"input/InputManager.h\"\n#include \"core/Engine.h\"\n\nvoid setup() {\n    // Configure input: 6 buttons\n    // Pins: Up=0, Down=2, Left=4, Right=5, A=18, B=19\n    pixelroot32::input::InputConfig inputConfig(6, 0, 2, 4, 5, 18, 19);\n\n    // Create input manager\n    pixelroot32::input::InputManager inputManager(inputConfig);\n    inputManager.init();\n\n    // Or use with Engine\n    pixelroot32::graphics::DisplayConfig displayConfig;\n    pixelroot32::core::Engine engine(displayConfig, inputConfig);\n    engine.init();\n    engine.run();\n}\n
    "},{"location":"api_reference/core/input_config/#native-configuration","title":"Native Configuration","text":"
    #include \"input/InputConfig.h\"\n#include <SDL2/SDL.h>\n\nvoid setup() {\n    // Configure input: 6 buttons mapped to keyboard\n    pixelroot32::input::InputConfig inputConfig(6,\n        SDL_SCANCODE_UP,    // Index 0: Up\n        SDL_SCANCODE_DOWN,  // Index 1: Down\n        SDL_SCANCODE_LEFT,  // Index 2: Left\n        SDL_SCANCODE_RIGHT, // Index 3: Right\n        SDL_SCANCODE_X,     // Index 4: Button A\n        SDL_SCANCODE_Z      // Index 5: Button B\n    );\n\n    // Use with Engine\n    pixelroot32::graphics::DisplayConfig displayConfig;\n    pixelroot32::core::Engine engine(displayConfig, inputConfig);\n    engine.init();\n    engine.run();\n}\n
    "},{"location":"api_reference/core/input_config/#platform-agnostic-configuration","title":"Platform-Agnostic Configuration","text":"
    #ifdef PLATFORM_ESP32\n    // ESP32: Use GPIO pins\n    pixelroot32::input::InputConfig inputConfig(6, 0, 2, 4, 5, 18, 19);\n#elif PLATFORM_NATIVE\n    // Native: Use SDL scancodes\n    #include <SDL2/SDL.h>\n    pixelroot32::input::InputConfig inputConfig(6,\n        SDL_SCANCODE_UP,\n        SDL_SCANCODE_DOWN,\n        SDL_SCANCODE_LEFT,\n        SDL_SCANCODE_RIGHT,\n        SDL_SCANCODE_X,\n        SDL_SCANCODE_Z\n    );\n#endif\n
    "},{"location":"api_reference/core/input_config/#button-index-mapping","title":"Button Index Mapping","text":"

    Button indices are determined by the order in the constructor:

    Typical Convention: - Index 0: Up / Primary action - Index 1: Down / Secondary action - Index 2: Left - Index 3: Right - Index 4+: Additional buttons

    Example:

    // 4-button D-pad\nInputConfig config(4, UP_PIN, DOWN_PIN, LEFT_PIN, RIGHT_PIN);\n// Index 0 = Up, Index 1 = Down, Index 2 = Left, Index 3 = Right\n\n// 6-button setup (D-pad + 2 action buttons)\nInputConfig config(6, UP_PIN, DOWN_PIN, LEFT_PIN, RIGHT_PIN, A_PIN, B_PIN);\n// Index 0-3 = D-pad, Index 4 = A, Index 5 = B\n

    "},{"location":"api_reference/core/input_config/#esp32-pin-considerations","title":"ESP32 Pin Considerations","text":"
    • GPIO pins: Use any available GPIO pin
    • Pull-up/pull-down: Configure resistors appropriately
    • Input mode: Pins are automatically configured as inputs
    • Restrictions: Some pins have special functions (check ESP32 datasheet)

    Common Pin Choices: - GPIO 0, 2, 4, 5: Safe for buttons (watch for boot mode pins) - GPIO 18, 19: Good for additional buttons - Avoid: GPIO 6-11 (flash), GPIO 34-39 (input only, no pull-up)

    "},{"location":"api_reference/core/input_config/#native-sdl-scancode-reference","title":"Native SDL Scancode Reference","text":"

    Common SDL scancodes:

    • SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT: Arrow keys
    • SDL_SCANCODE_W, SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_D: WASD
    • SDL_SCANCODE_X, SDL_SCANCODE_Z: Common action buttons
    • SDL_SCANCODE_SPACE: Spacebar
    • SDL_SCANCODE_RETURN: Enter key

    Example:

    // WASD + Space + Enter\npixelroot32::input::InputConfig config(6,\n    SDL_SCANCODE_W,        // Up\n    SDL_SCANCODE_S,        // Down\n    SDL_SCANCODE_A,        // Left\n    SDL_SCANCODE_D,         // Right\n    SDL_SCANCODE_SPACE,    // Jump\n    SDL_SCANCODE_RETURN    // Action\n);\n

    "},{"location":"api_reference/core/input_config/#performance-considerations","title":"Performance Considerations","text":"
    • Memory: Arrays are allocated dynamically (small overhead)
    • Configuration: Done once at startup, no runtime cost
    • Access: Button indices are fast (array access)
    "},{"location":"api_reference/core/input_config/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Pin configuration: Ensure pins are not used by other peripherals
    • Debouncing: Hardware debouncing recommended for reliable input
    • Power: Buttons should use pull-up resistors to avoid floating pins
    "},{"location":"api_reference/core/input_config/#see-also","title":"See Also","text":"
    • InputManager - Input handling
    • Engine - Engine that uses InputConfig
    • Manual - Input and Control
    • API Overview
    "},{"location":"api_reference/core/input_manager/","title":"InputManager","text":"

    Handles input from physical buttons or keyboard (on PC).

    "},{"location":"api_reference/core/input_manager/#description","title":"Description","text":"

    The InputManager polls configured pins (ESP32) or keyboard state (Native), handles debouncing, and tracks button states (Pressed, Released, Down, Clicked). It provides a unified input interface for both platforms.

    The manager supports edge detection (just pressed/released) and continuous state (held down), making it suitable for both gameplay and UI navigation.

    "},{"location":"api_reference/core/input_manager/#namespace","title":"Namespace","text":"
    namespace pixelroot32::input {\n    class InputManager {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/input_manager/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Engine (manages input manager instance)
    "},{"location":"api_reference/core/input_manager/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/input_manager/#inputmanagerconst-inputconfig-config","title":"InputManager(const InputConfig& config)","text":"

    Constructs the InputManager with a specific configuration.

    Parameters: - config (const InputConfig&): The input configuration (pins, button count)

    Example:

    #include \"input/InputManager.h\"\n#include \"input/InputConfig.h\"\n\n// ESP32: Configure GPIO pins\npixelroot32::input::InputConfig inputConfig(6, 0, 2, 4, 5, 18, 19);\npixelroot32::input::InputManager inputManager(inputConfig);\ninputManager.init();\n\n// Native: Configure keyboard keys\n// (Configuration handled differently on Native)\n

    "},{"location":"api_reference/core/input_manager/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/input_manager/#void-init","title":"void init()","text":"

    Initializes the input pins.

    Returns: - void

    Notes: - Must be called after construction and before use - Configures GPIO pins (ESP32) or keyboard state (Native) - Safe to call multiple times (idempotent) - Typically called automatically by Engine::init()

    Example:

    InputManager inputManager(inputConfig);\ninputManager.init();  // Initialize before use\n

    "},{"location":"api_reference/core/input_manager/#void-updateunsigned-long-dt","title":"void update(unsigned long dt)","text":"

    Updates input state by polling hardware pins (ESP32) or keyboard state (Native).

    Parameters: - dt (unsigned long): Delta time in milliseconds

    Returns: - void

    Notes: - Must be called every frame for proper input detection - Handles debouncing automatically - Updates button states and edge detection - Typically called automatically by Engine::update()

    ESP32 Example:

    void update(unsigned long deltaTime) override {\n    // Input is updated automatically by Engine\n    // Access input via engine.getInputManager()\n}\n

    Native Example:

    // On Native, update is called with keyboard state:\nvoid update(unsigned long dt, const uint8_t* keyboardState);\n

    "},{"location":"api_reference/core/input_manager/#bool-isbuttonpresseduint8_t-buttonindex-const","title":"bool isButtonPressed(uint8_t buttonIndex) const","text":"

    Checks if a button was just pressed this frame.

    Parameters: - buttonIndex (uint8_t): Index of the button to check (0-based)

    Returns: - bool: true if the button transitioned from UP to DOWN this frame

    Notes: - Returns true only on the frame the button was pressed - Useful for one-time actions (jump, shoot, menu select) - Resets automatically on next frame

    Example:

    auto& input = engine.getInputManager();\n\nif (input.isButtonPressed(0)) {  // Button A (index 0)\n    // Jump (only once per press)\n    player->jump();\n}\n\nif (input.isButtonPressed(1)) {  // Button B (index 1)\n    // Shoot (only once per press)\n    player->shoot();\n}\n

    "},{"location":"api_reference/core/input_manager/#bool-isbuttonreleaseduint8_t-buttonindex-const","title":"bool isButtonReleased(uint8_t buttonIndex) const","text":"

    Checks if a button was just released this frame.

    Parameters: - buttonIndex (uint8_t): Index of the button to check

    Returns: - bool: true if the button transitioned from DOWN to UP this frame

    Notes: - Returns true only on the frame the button was released - Useful for detecting button release events - Less commonly used than isButtonPressed()

    Example:

    auto& input = engine.getInputManager();\n\nif (input.isButtonReleased(0)) {\n    // Button A was just released\n    player->stopCharging();\n}\n

    "},{"location":"api_reference/core/input_manager/#bool-isbuttonclickeduint8_t-buttonindex-const","title":"bool isButtonClicked(uint8_t buttonIndex) const","text":"

    Checks if a button was clicked (pressed and released).

    Parameters: - buttonIndex (uint8_t): Index of the button to check

    Returns: - bool: true if the button was clicked (pressed then released)

    Notes: - Returns true when button is released after being pressed - Useful for UI buttons and menu selection - Detects complete press-release cycle

    Example:

    auto& input = engine.getInputManager();\n\nif (input.isButtonClicked(0)) {  // Button A clicked\n    // Select menu item\n    menu->select();\n}\n

    "},{"location":"api_reference/core/input_manager/#bool-isbuttondownuint8_t-buttonindex-const","title":"bool isButtonDown(uint8_t buttonIndex) const","text":"

    Checks if a button is currently held down.

    Parameters: - buttonIndex (uint8_t): Index of the button to check

    Returns: - bool: true if the button is currently in the DOWN state

    Notes: - Returns true for as long as the button is held - Useful for continuous actions (movement, charging) - Use with deltaTime for frame-rate independent movement

    Example:

    auto& input = engine.getInputManager();\n\nfloat speed = 100.0f;  // pixels per second\nfloat vx = 0.0f, vy = 0.0f;\n\nif (input.isButtonDown(2)) {  // Left button\n    vx = -speed;\n}\nif (input.isButtonDown(3)) {  // Right button\n    vx = speed;\n}\nif (input.isButtonDown(0)) {  // Up button\n    vy = -speed;\n}\nif (input.isButtonDown(1)) {  // Down button\n    vy = speed;\n}\n\n// Apply movement (frame-rate independent)\nx += (vx * deltaTime) / 1000.0f;\ny += (vy * deltaTime) / 1000.0f;\n

    "},{"location":"api_reference/core/input_manager/#button-indices","title":"Button Indices","text":"

    Button indices are defined by the order in InputConfig:

    Typical Mapping: - 0: Up / Button A - 1: Down / Button B - 2: Left - 3: Right - 4: Additional button 1 - 5: Additional button 2

    Example:

    // Configure 6 buttons: Up, Down, Left, Right, A, B\npixelroot32::input::InputConfig inputConfig(6, \n    GPIO_UP,    // Index 0\n    GPIO_DOWN,  // Index 1\n    GPIO_LEFT,  // Index 2\n    GPIO_RIGHT, // Index 3\n    GPIO_A,     // Index 4\n    GPIO_B      // Index 5\n);\n\n// Use indices\nif (input.isButtonDown(2)) {  // Left\n    moveLeft();\n}\nif (input.isButtonPressed(4)) {  // A button\n    jump();\n}\n

    "},{"location":"api_reference/core/input_manager/#usage-example","title":"Usage Example","text":"
    #include \"input/InputManager.h\"\n#include \"core/Engine.h\"\n\nclass PlayerController {\nprivate:\n    pixelroot32::core::Engine& engine;\n\npublic:\n    PlayerController(pixelroot32::core::Engine& eng) : engine(eng) {}\n\n    void update(unsigned long deltaTime) {\n        auto& input = engine.getInputManager();\n\n        // Movement (continuous)\n        float speed = 150.0f;  // pixels per second\n        float vx = 0.0f, vy = 0.0f;\n\n        if (input.isButtonDown(3)) {  // Right\n            vx = speed;\n        }\n        if (input.isButtonDown(2)) {  // Left\n            vx = -speed;\n        }\n        if (input.isButtonDown(0)) {  // Up\n            vy = -speed;\n        }\n        if (input.isButtonDown(1)) {  // Down\n            vy = speed;\n        }\n\n        // Apply movement\n        playerX += (vx * deltaTime) / 1000.0f;\n        playerY += (vy * deltaTime) / 1000.0f;\n\n        // Actions (one-time)\n        if (input.isButtonPressed(4)) {  // A button\n            player->jump();\n        }\n\n        if (input.isButtonPressed(5)) {  // B button\n            player->shoot();\n        }\n    }\n};\n
    "},{"location":"api_reference/core/input_manager/#input-state-comparison","title":"Input State Comparison","text":"Method Returns true when Use Case isButtonPressed() Button just pressed this frame One-time actions (jump, shoot) isButtonReleased() Button just released this frame Release events (stop charging) isButtonClicked() Button pressed then released UI buttons, menu selection isButtonDown() Button currently held Continuous actions (movement)"},{"location":"api_reference/core/input_manager/#performance-considerations","title":"Performance Considerations","text":"
    • Update frequency: update() must be called every frame
    • Debouncing: Handled automatically, no performance impact
    • State queries: All query methods are fast (inline accessors)
    • Memory: Button state arrays are small and efficient
    "},{"location":"api_reference/core/input_manager/#esp32-considerations","title":"ESP32 Considerations","text":"
    • GPIO pins: Configure pins in InputConfig
    • Pull-up/pull-down: Ensure proper resistor configuration
    • Debouncing: Hardware debouncing recommended for noisy buttons
    • Pin limits: Some ESP32 pins have restrictions (check datasheet)
    "},{"location":"api_reference/core/input_manager/#native-considerations","title":"Native Considerations","text":"
    • Keyboard mapping: Uses SDL scancodes
    • Key detection: Automatically handles keyboard state
    • Multiple keys: Can detect multiple keys simultaneously
    "},{"location":"api_reference/core/input_manager/#see-also","title":"See Also","text":"
    • InputConfig - Input configuration
    • Engine - Engine that manages InputManager
    • Manual - Input and Control
    • API Overview
    "},{"location":"api_reference/core/physics_actor/","title":"PhysicsActor","text":"

    An actor with basic 2D physics properties.

    "},{"location":"api_reference/core/physics_actor/#description","title":"Description","text":"

    PhysicsActor extends the base Actor class by adding velocity, acceleration, friction, restitution (bounciness), and world boundary collision resolution. It is designed for objects that need to move and bounce within a defined area, such as balls, projectiles, or platformer characters.

    PhysicsActor automatically handles: - Velocity-based movement - Friction application - World boundary collision and bouncing - Collision callbacks

    "},{"location":"api_reference/core/physics_actor/#namespace","title":"Namespace","text":"
    namespace pixelroot32::core {\n    class PhysicsActor : public Actor {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/physics_actor/#inheritance","title":"Inheritance","text":"
    • Inherits from: Actor
    • Inherited by: Your custom physics-enabled actor classes
    "},{"location":"api_reference/core/physics_actor/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/physics_actor/#physicsactorfloat-x-float-y-float-w-float-h","title":"PhysicsActor(float x, float y, float w, float h)","text":"

    Creates a physics-enabled actor with specified position and size.

    Parameters: - x (float): Initial X position in world space - y (float): Initial Y position in world space - w (float): Actor width in pixels - h (float): Actor height in pixels

    Notes: - Velocity starts at (0, 0) - Restitution defaults to 1.0 (perfect bounce) - Friction defaults to 0.0 (no friction) - No world limits by default

    Example:

    class BallActor : public pixelroot32::core::PhysicsActor {\npublic:\n    BallActor(float x, float y) \n        : PhysicsActor(x, y, 8.0f, 8.0f) {\n        // Set physics properties\n        setRestitution(0.8f);  // 80% bounce\n        setFriction(0.1f);     // Small friction\n        setWorldSize(128, 128); // World bounds\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledCircle(static_cast<int>(x + width/2), \n                                 static_cast<int>(y + height/2), \n                                 width/2, \n                                 Color::White);\n    }\n\n    Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(Actor* other) override {\n        // Bounce off other actors\n    }\n\n    void onWorldCollision() override {\n        // Play bounce sound\n    }\n};\n

    "},{"location":"api_reference/core/physics_actor/#protected-properties","title":"Protected Properties","text":""},{"location":"api_reference/core/physics_actor/#float-vx-vy","title":"float vx, vy","text":"

    Horizontal and vertical velocity components.

    Type: float

    Access: Protected (use setVelocity() to modify)

    Default: 0.0f

    Notes: - Velocity is in pixels per second - Automatically applied during update() - Modified by friction and world collisions

    "},{"location":"api_reference/core/physics_actor/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/physics_actor/#void-setvelocityfloat-x-float-y","title":"void setVelocity(float x, float y)","text":"

    Sets the linear velocity of the actor.

    Parameters: - x (float): Horizontal velocity in pixels per second - y (float): Vertical velocity in pixels per second

    Returns: - void

    Notes: - Velocity is applied every frame during update() - Use for initial velocity or impulse-based movement - Can be called every frame for continuous control

    Example:

    // Set initial velocity\nphysicsActor->setVelocity(100.0f, -200.0f);  // Move right and up\n\n// Continuous control (e.g., player movement)\nvoid update(unsigned long deltaTime) override {\n    PhysicsActor::update(deltaTime);\n\n    float speed = 150.0f;\n    float vx = 0.0f, vy = 0.0f;\n\n    if (input.isButtonDown(Buttons::LEFT)) vx = -speed;\n    if (input.isButtonDown(Buttons::RIGHT)) vx = speed;\n    if (input.isButtonDown(Buttons::UP)) vy = -speed;\n    if (input.isButtonDown(Buttons::DOWN)) vy = speed;\n\n    setVelocity(vx, vy);\n}\n

    "},{"location":"api_reference/core/physics_actor/#void-setrestitutionfloat-r","title":"void setRestitution(float r)","text":"

    Sets the restitution (bounciness) of the actor.

    Parameters: - r (float): Restitution value (0.0 to 1.0+) - 0.0: No bounce (stops on impact) - 1.0: Perfect bounce (no energy loss) - > 1.0: Energy gain (unrealistic but possible)

    Returns: - void

    Notes: - Applied when actor collides with world boundaries - Higher values = more bouncy - Typical values: 0.5-0.9 for realistic bouncing

    Example:

    ball->setRestitution(0.8f);  // 80% bounce\n

    "},{"location":"api_reference/core/physics_actor/#void-setfrictionfloat-f","title":"void setFriction(float f)","text":"

    Sets the friction coefficient.

    Parameters: - f (float): Friction value - 0.0: No friction (object continues moving) - > 0.0: Friction applied to velocity each frame

    Returns: - void

    Notes: - Applied every frame to reduce velocity - Higher values = more friction (slower movement) - Typical values: 0.05-0.2 for smooth deceleration

    Example:

    player->setFriction(0.1f);  // Light friction\n

    "},{"location":"api_reference/core/physics_actor/#void-setlimitslimitrect-limits","title":"void setLimits(LimitRect limits)","text":"

    Sets custom movement limits for the actor.

    Parameters: - limits (LimitRect): A rectangle defining the allowed area

    Returns: - void

    Notes: - Overrides world size limits - Use -1 for any boundary to disable that limit - Actor will bounce off these boundaries

    Example:

    pixelroot32::core::LimitRect limits;\nlimits.left = 0;\nlimits.top = 0;\nlimits.right = 128;\nlimits.bottom = 128;\nphysicsActor->setLimits(limits);\n

    "},{"location":"api_reference/core/physics_actor/#void-setworldsizeint-width-int-height","title":"void setWorldSize(int width, int height)","text":"

    Defines the world size for boundary checking.

    Parameters: - width (int): Width of the world in pixels - height (int): Height of the world in pixels

    Returns: - void

    Notes: - Used as default limits if no custom LimitRect is provided - Actor will bounce off world boundaries - Set to display size for screen boundaries

    Example:

    physicsActor->setWorldSize(128, 128);  // Match display size\n

    "},{"location":"api_reference/core/physics_actor/#worldcollisioninfo-getworldcollisioninfo-const","title":"WorldCollisionInfo getWorldCollisionInfo() const","text":"

    Gets information about collisions with the world boundaries.

    Returns: - WorldCollisionInfo: A struct containing collision flags (left, right, top, bottom)

    Notes: - Updated every frame during update() - Use to detect which boundary was hit - Useful for sound effects or special behaviors

    Example:

    void update(unsigned long deltaTime) override {\n    PhysicsActor::update(deltaTime);\n\n    auto collision = getWorldCollisionInfo();\n    if (collision.left || collision.right) {\n        // Hit side wall\n        playSound(wallHitSound);\n    }\n    if (collision.top || collision.bottom) {\n        // Hit top or bottom\n        playSound(ceilingHitSound);\n    }\n}\n

    "},{"location":"api_reference/core/physics_actor/#virtual-void-oncollisionactor-other-override","title":"virtual void onCollision(Actor* other) override","text":"

    Callback triggered when this actor collides with another actor.

    Parameters: - other (Actor*): Pointer to the actor involved in the collision

    Returns: - void

    Notes: - Called automatically by the collision system - Override to implement custom collision responses - Default implementation does nothing

    Example:

    void onCollision(Actor* other) override {\n    if (other->isInLayer(DefaultLayers::kEnemy)) {\n        // Bounce off enemy\n        vx = -vx * 0.5f;\n        vy = -vy * 0.5f;\n    }\n}\n

    "},{"location":"api_reference/core/physics_actor/#virtual-void-onworldcollision","title":"virtual void onWorldCollision()","text":"

    Callback triggered when this actor collides with world boundaries.

    Returns: - void

    Notes: - Called automatically when a world boundary collision occurs - Override to implement custom behavior (sound effects, particles, etc.) - Default implementation does nothing

    Example:

    void onWorldCollision() override {\n    // Play bounce sound\n    auto& audio = engine.getAudioEngine();\n    pixelroot32::audio::AudioEvent sound{};\n    sound.type = pixelroot32::audio::WaveType::NOISE;\n    sound.frequency = 500.0f;\n    sound.duration = 0.05f;\n    audio.playEvent(sound);\n\n    // Spawn particles\n    spawnBounceParticles();\n}\n

    "},{"location":"api_reference/core/physics_actor/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the actor state. Applies physics integration and checks for world boundary collisions.

    Parameters: - deltaTime (unsigned long): Time elapsed since the last frame in milliseconds

    Returns: - void

    Notes: - Called automatically by Scene if isEnabled is true - Applies velocity to position - Applies friction to velocity - Resolves world boundary collisions - Override to add custom update logic, but call PhysicsActor::update(deltaTime) first

    Example:

    void update(unsigned long deltaTime) override {\n    // Apply physics\n    PhysicsActor::update(deltaTime);\n\n    // Custom logic\n    if (shouldApplyGravity) {\n        vy += gravity * (deltaTime / 1000.0f);\n    }\n}\n

    "},{"location":"api_reference/core/physics_actor/#limitrect-structure","title":"LimitRect Structure","text":"

    Bounding rectangle for world-collision resolution.

    Members: - int left: Left boundary (-1 means no limit) - int top: Top boundary (-1 means no limit) - int right: Right boundary (-1 means no limit) - int bottom: Bottom boundary (-1 means no limit)

    Methods: - int width() const: Calculates width (right - left) - int height() const: Calculates height (bottom - top)

    Example:

    pixelroot32::core::LimitRect limits(10, 10, 118, 118);  // 10px margin\nphysicsActor->setLimits(limits);\n

    "},{"location":"api_reference/core/physics_actor/#worldcollisioninfo-structure","title":"WorldCollisionInfo Structure","text":"

    Information about world collisions in the current frame.

    Members: - bool left: True if collided with the left boundary - bool right: True if collided with the right boundary - bool top: True if collided with the top boundary - bool bottom: True if collided with the bottom boundary

    Example:

    auto collision = physicsActor->getWorldCollisionInfo();\nif (collision.bottom) {\n    // On ground\n    canJump = true;\n}\n

    "},{"location":"api_reference/core/physics_actor/#usage-example","title":"Usage Example","text":"
    #include \"core/PhysicsActor.h\"\n\nclass BouncingBall : public pixelroot32::core::PhysicsActor {\npublic:\n    BouncingBall(float x, float y) \n        : PhysicsActor(x, y, 8.0f, 8.0f) {\n        // Set physics properties\n        setRestitution(0.9f);  // Very bouncy\n        setFriction(0.05f);    // Light friction\n        setWorldSize(128, 128);\n\n        // Set initial velocity\n        setVelocity(100.0f, -150.0f);\n\n        // Set collision layer\n        layer = pixelroot32::physics::DefaultLayers::kProjectile;\n        mask = pixelroot32::physics::DefaultLayers::kObstacle;\n    }\n\n    void update(unsigned long deltaTime) override {\n        PhysicsActor::update(deltaTime);\n\n        // Apply gravity\n        vy += 200.0f * (deltaTime / 1000.0f);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledCircle(static_cast<int>(x + width/2), \n                                 static_cast<int>(y + height/2), \n                                 width/2, \n                                 Color::White);\n    }\n\n    Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(Actor* other) override {\n        // Bounce off obstacles\n        vx = -vx * 0.8f;\n        vy = -vy * 0.8f;\n    }\n\n    void onWorldCollision() override {\n        // Play bounce sound\n        playBounceSound();\n    }\n};\n
    "},{"location":"api_reference/core/physics_actor/#performance-considerations","title":"Performance Considerations","text":"
    • Physics integration: Very efficient (simple velocity integration)
    • World bounds: Boundary checks are fast (AABB)
    • Friction: Applied every frame; keep friction values reasonable
    • Collision callbacks: Keep onCollision() and onWorldCollision() fast
    "},{"location":"api_reference/core/physics_actor/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Floating point: Uses float math; acceptable for ESP32 but integer math would be faster
    • Frame rate: Physics is frame-rate independent (uses deltaTime)
    • Memory: Each PhysicsActor consumes more memory than Actor (velocity, limits, etc.)
    "},{"location":"api_reference/core/physics_actor/#see-also","title":"See Also","text":"
    • Actor - Base actor class
    • CollisionSystem - Collision detection
    • Manual - Physics and Collisions
    • API Overview
    "},{"location":"api_reference/core/scene/","title":"Scene","text":"

    Represents a game level or screen containing entities.

    "},{"location":"api_reference/core/scene/#description","title":"Description","text":"

    A Scene manages a collection of Entities and a CollisionSystem. It is responsible for updating and drawing all entities it contains. Scenes provide lifecycle hooks (init(), update(), draw()) to manage gameplay segments.

    Scenes are the primary organizational unit in PixelRoot32, similar to levels or screens in other game engines. Each scene can contain up to MAX_ENTITIES (32) entities.

    "},{"location":"api_reference/core/scene/#namespace","title":"Namespace","text":"
    namespace pixelroot32::core {\n    class Scene {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/scene/#inheritance","title":"Inheritance","text":"
    • Base class: None (abstract base class)
    • Inherited by: Your custom scene classes (e.g., MainMenuScene, GameScene)
    "},{"location":"api_reference/core/scene/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/scene/#scene_1","title":"Scene()","text":"

    Creates an empty scene ready to be populated with entities.

    Notes: - The scene starts with no entities - init() should be called when the scene becomes active - The collision system is automatically initialized

    Example:

    class MyScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Initialize scene resources\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);  // Update entities and collisions\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        Scene::draw(renderer);  // Draw all entities\n    }\n};\n

    "},{"location":"api_reference/core/scene/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/scene/#virtual-void-init","title":"virtual void init()","text":"

    Initializes the scene. Called when entering the scene.

    Returns: - void

    Notes: - Called automatically when the scene is set via Engine::setScene() - Override this method to initialize scene-specific resources - Safe to call multiple times (idempotent) - Add entities here or in the constructor

    Example:

    class GameScene : public pixelroot32::core::Scene {\nprivate:\n    PlayerActor* player;\n    std::array<EnemyActor*, 10> enemies;\n\npublic:\n    void init() override {\n        // Create player\n        player = new PlayerActor();\n        addEntity(player);\n\n        // Create enemies\n        for (int i = 0; i < 10; i++) {\n            enemies[i] = new EnemyActor();\n            addEntity(enemies[i]);\n        }\n    }\n};\n

    "},{"location":"api_reference/core/scene/#virtual-void-updateunsigned-long-deltatime","title":"virtual void update(unsigned long deltaTime)","text":"

    Updates all entities in the scene and handles collisions.

    Parameters: - deltaTime (unsigned long): Time elapsed since last frame in milliseconds

    Notes: - Called automatically by the engine every frame - Updates all entities in the scene - Processes collisions between actors - Override to add custom update logic, but call Scene::update(deltaTime) to maintain entity updates

    Example:

    void update(unsigned long deltaTime) override {\n    // Custom update logic\n    gameTimer += deltaTime;\n\n    // Update entities and collisions\n    Scene::update(deltaTime);\n\n    // Additional logic after entity updates\n    if (gameTimer > 60000) {\n        // Game over after 60 seconds\n    }\n}\n

    "},{"location":"api_reference/core/scene/#virtual-void-drawrenderer-renderer","title":"virtual void draw(Renderer& renderer)","text":"

    Draws all visible entities in the scene.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): The renderer to use for drawing

    Notes: - Called automatically by the engine every frame after update() - Draws all visible entities in the scene - Override to add custom drawing logic, but call Scene::draw(renderer) to maintain entity rendering - Entities are drawn in the order they were added

    Example:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Draw background\n    renderer.drawTileMap(backgroundTileMap, 0, 0, Color::White);\n\n    // Draw all entities\n    Scene::draw(renderer);\n\n    // Draw UI overlay\n    renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n}\n

    "},{"location":"api_reference/core/scene/#void-addentityentity-entity","title":"void addEntity(Entity* entity)","text":"

    Adds an entity to the scene.

    Parameters: - entity (Entity*): Pointer to the Entity to add. Must not be nullptr.

    Notes: - Entities are added to an internal queue - Maximum of MAX_ENTITIES (32) entities per scene - If the limit is reached, the entity may not be added (check return value if available) - Entities are updated and drawn in the order they were added - The entity's lifetime is managed by the scene (do not delete manually while in scene)

    Example:

    void init() override {\n    // Create and add player\n    PlayerActor* player = new PlayerActor();\n    player->setPosition(64, 64);\n    addEntity(player);\n\n    // Create and add enemy\n    EnemyActor* enemy = new EnemyActor();\n    enemy->setPosition(100, 100);\n    addEntity(enemy);\n}\n

    "},{"location":"api_reference/core/scene/#void-removeentityentity-entity","title":"void removeEntity(Entity* entity)","text":"

    Removes an entity from the scene.

    Parameters: - entity (Entity*): Pointer to the Entity to remove

    Notes: - The entity is removed from the update and draw queues - The entity is not deleted automatically (you must manage its lifetime) - Safe to call even if the entity is not in the scene - Consider using object pooling instead of frequent add/remove

    Example:

    void onEnemyDestroyed(EnemyActor* enemy) {\n    removeEntity(enemy);\n    // Return to pool or delete\n    enemyPool.returnToPool(enemy);\n}\n

    "},{"location":"api_reference/core/scene/#void-clearentities","title":"void clearEntities()","text":"

    Removes all entities from the scene.

    Notes: - All entities are removed from the update and draw queues - Entities are not deleted automatically (you must manage their lifetimes) - Useful for scene cleanup or reset - Consider using object pooling to reuse entities

    Example:

    void reset() {\n    clearEntities();\n    // Return all entities to pool\n    for (auto* entity : entityPool) {\n        entityPool.returnToPool(entity);\n    }\n}\n

    "},{"location":"api_reference/core/scene/#protected-members","title":"Protected Members","text":""},{"location":"api_reference/core/scene/#arduinoqueue-entities","title":"ArduinoQueue entities

    Queue of entities in the scene. Accessible to derived classes for custom entity management.

    Type: ArduinoQueue<Entity*>

    Notes: - Maximum capacity: MAX_ENTITIES (32) - Direct access allows custom iteration or filtering - Use with caution: modifying while iterating may cause issues

    ","text":""},{"location":"api_reference/core/scene/#collisionsystem-collisionsystem","title":"CollisionSystem collisionSystem

    System to handle collisions between actors. Accessible to derived classes for custom collision handling.

    Type: pixelroot32::physics::CollisionSystem

    Notes: - Automatically processes collisions between actors - Uses collision layers and masks for filtering - Can be accessed for custom collision queries

    ","text":""},{"location":"api_reference/core/scene/#max_entities-constant","title":"MAX_ENTITIES Constant","text":"

    The maximum number of entities allowed per scene.

    Value: 32

    Notes: - Hard limit: cannot be changed without modifying engine code - Includes all entity types: actors, UI elements, particles, etc. - Plan your entity usage carefully - Use object pooling to reuse entities instead of creating new ones

    "},{"location":"api_reference/core/scene/#usage-example","title":"Usage Example","text":"
    #include \"core/Scene.h\"\n#include \"core/Actor.h\"\n\nclass MyGameScene : public pixelroot32::core::Scene {\nprivate:\n    PlayerActor* player;\n\npublic:\n    void init() override {\n        // Create player\n        player = new PlayerActor();\n        player->setPosition(64, 64);\n        addEntity(player);\n\n        // Create some enemies\n        for (int i = 0; i < 5; i++) {\n            EnemyActor* enemy = new EnemyActor();\n            enemy->setPosition(10 + i * 20, 10);\n            addEntity(enemy);\n        }\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Custom game logic\n        if (player->isDead()) {\n            // Handle game over\n        }\n\n        // Update entities and collisions\n        Scene::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw background\n        renderer.drawFilledRectangle(0, 0, 128, 128, Color::Black);\n\n        // Draw all entities\n        Scene::draw(renderer);\n\n        // Draw HUD\n        char scoreText[32];\n        snprintf(scoreText, sizeof(scoreText), \"Score: %d\", score);\n        renderer.drawText(scoreText, 10, 10, Color::White, 1);\n    }\n};\n
    "},{"location":"api_reference/core/scene/#performance-considerations","title":"Performance Considerations","text":"
    • Entity limit: MAX_ENTITIES = 32 is a hard limit; plan accordingly
    • Add/Remove: Frequent add/remove operations can be expensive; use object pooling
    • Update order: Entities are updated in add order; consider order for dependencies
    • Collision checks: CollisionSystem automatically handles actor collisions efficiently
    "},{"location":"api_reference/core/scene/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Each entity consumes memory; stay well below the limit
    • Object pooling: Essential for ESP32 to avoid memory fragmentation
    • Scene switching: Clearing and recreating scenes can fragment memory; reuse scenes when possible
    "},{"location":"api_reference/core/scene/#see-also","title":"See Also","text":"
    • Entity - Base entity class
    • Actor - Entity with collision support
    • PhysicsActor - Entity with physics
    • CollisionSystem - Collision detection
    • Manual - Scenes and Entities
    • API Overview
    "},{"location":"api_reference/graphics/camera2d/","title":"Camera2D","text":"

    2D camera for scrolling and viewport control.

    "},{"location":"api_reference/graphics/camera2d/#description","title":"Description","text":"

    Camera2D controls viewport position and enables scrolling by shifting the renderer's display offset. It supports following targets, boundary constraints, and can be used for parallax effects.

    The camera uses a dead-zone system: it only moves when the target is outside a central zone, creating smooth following behavior.

    "},{"location":"api_reference/graphics/camera2d/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    class Camera2D {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/camera2d/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Scenes (for scrolling and camera control)
    "},{"location":"api_reference/graphics/camera2d/#constructors","title":"Constructors","text":""},{"location":"api_reference/graphics/camera2d/#camera2dint-viewportwidth-int-viewportheight","title":"Camera2D(int viewportWidth, int viewportHeight)","text":"

    Creates a new camera with specified viewport dimensions.

    Parameters: - viewportWidth (int): Width of the viewport in pixels - viewportHeight (int): Height of the viewport in pixels

    Notes: - Viewport size should match display size - Camera position starts at (0, 0) - No boundaries set by default (camera can move anywhere)

    Example:

    #include \"graphics/Camera2D.h\"\n\n// Create camera matching display size\npixelroot32::graphics::Camera2D camera(128, 128);\n\n// Or get from renderer\nint width = renderer.getWidth();\nint height = renderer.getHeight();\npixelroot32::graphics::Camera2D camera(width, height);\n

    "},{"location":"api_reference/graphics/camera2d/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/graphics/camera2d/#void-setpositionfloat-x-float-y","title":"void setPosition(float x, float y)","text":"

    Sets the camera position directly.

    Parameters: - x (float): X position in world space - y (float): Y position in world space

    Returns: - void

    Notes: - Position is clamped to boundaries if set - Use for direct camera control or cutscenes - Overrides any following behavior

    Example:

    camera.setPosition(100.0f, 200.0f);\n

    "},{"location":"api_reference/graphics/camera2d/#void-setboundsfloat-minx-float-maxx","title":"void setBounds(float minX, float maxX)","text":"

    Sets horizontal boundaries for the camera.

    Parameters: - minX (float): Minimum X position - maxX (float): Maximum X position

    Returns: - void

    Notes: - Camera position is clamped to these bounds - Use to prevent camera from going outside level bounds - Set both horizontal and vertical bounds for full constraint

    Example:

    // Level is 512 pixels wide, camera viewport is 128\n// Prevent camera from showing outside level\ncamera.setBounds(0.0f, 512.0f - 128.0f);\n

    "},{"location":"api_reference/graphics/camera2d/#void-setverticalboundsfloat-miny-float-maxy","title":"void setVerticalBounds(float minY, float maxY)","text":"

    Sets vertical boundaries for the camera.

    Parameters: - minY (float): Minimum Y position - maxY (float): Maximum Y position

    Returns: - void

    Notes: - Camera position is clamped to these bounds - Use to prevent camera from going outside level bounds vertically

    Example:

    // Level is 512 pixels tall, camera viewport is 128\ncamera.setVerticalBounds(0.0f, 512.0f - 128.0f);\n

    "},{"location":"api_reference/graphics/camera2d/#void-followtargetfloat-targetx","title":"void followTarget(float targetX)","text":"

    Makes the camera follow a target horizontally only.

    Parameters: - targetX (float): X position of the target to follow

    Returns: - void

    Notes: - Camera follows target with dead-zone behavior - Only horizontal movement; vertical position unchanged - Useful for side-scrolling games

    Example:

    void update(unsigned long deltaTime) override {\n    // Update player position\n    player->update(deltaTime);\n\n    // Camera follows player horizontally\n    camera.followTarget(player->x);\n    camera.apply(renderer);\n}\n

    "},{"location":"api_reference/graphics/camera2d/#void-followtargetfloat-targetx-float-targety","title":"void followTarget(float targetX, float targetY)","text":"

    Makes the camera follow a target in both axes.

    Parameters: - targetX (float): X position of the target to follow - targetY (float): Y position of the target to follow

    Returns: - void

    Notes: - Camera follows target with dead-zone behavior - Both horizontal and vertical following - Useful for top-down or platformer games

    Example:

    void update(unsigned long deltaTime) override {\n    player->update(deltaTime);\n\n    // Camera follows player in both axes\n    camera.followTarget(player->x, player->y);\n    camera.apply(renderer);\n}\n

    "},{"location":"api_reference/graphics/camera2d/#float-getx-const","title":"float getX() const","text":"

    Gets the current X position of the camera.

    Returns: - float: Current X position in world space

    Example:

    float cameraX = camera.getX();\n

    "},{"location":"api_reference/graphics/camera2d/#float-gety-const","title":"float getY() const","text":"

    Gets the current Y position of the camera.

    Returns: - float: Current Y position in world space

    Example:

    float cameraY = camera.getY();\n

    "},{"location":"api_reference/graphics/camera2d/#void-applyrenderer-renderer-const","title":"void apply(Renderer& renderer) const","text":"

    Applies the camera's offset to the renderer.

    Parameters: - renderer (Renderer&): The renderer to apply the camera offset to

    Returns: - void

    Notes: - Sets the renderer's display offset based on camera position - Should be called before drawing world elements - Negative offset is applied (camera moves right = world moves left)

    Example:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Apply camera offset\n    camera.apply(renderer);\n\n    // Draw world (offset applied automatically)\n    renderer.drawTileMap(levelMap, 0, 0, Color::White);\n    renderer.drawSprite(playerSprite, playerX, playerY, Color::White);\n\n    // UI elements (not affected by camera)\n    renderer.setDisplayOffset(0, 0);  // Reset for UI\n    renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n}\n

    "},{"location":"api_reference/graphics/camera2d/#dead-zone-following","title":"Dead-Zone Following","text":"

    The camera uses a dead-zone system for smooth following:

    • Dead zone: Central area where camera doesn't move
    • Following: Camera moves only when target leaves dead zone
    • Smooth: Creates natural, non-jarring camera movement

    Example:

    // Camera follows player with dead zone\nvoid update(unsigned long deltaTime) override {\n    player->update(deltaTime);\n\n    // Camera follows (dead zone handled internally)\n    camera.followTarget(player->x, player->y);\n}\n

    "},{"location":"api_reference/graphics/camera2d/#usage-example","title":"Usage Example","text":"
    #include \"graphics/Camera2D.h\"\n\nclass GameScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    PlayerActor* player;\n    TileMap levelMap;\n\npublic:\n    void init() override {\n        // Create camera matching display size\n        auto& renderer = engine.getRenderer();\n        camera = pixelroot32::graphics::Camera2D(\n            renderer.getWidth(), \n            renderer.getHeight()\n        );\n\n        // Set level boundaries\n        // Level is 512x512, viewport is 128x128\n        camera.setBounds(0.0f, 512.0f - 128.0f);\n        camera.setVerticalBounds(0.0f, 512.0f - 128.0f);\n\n        // Create player\n        player = new PlayerActor(64, 64);\n        addEntity(player);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Camera follows player\n        camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Apply camera\n        camera.apply(renderer);\n\n        // Draw world (camera offset applied)\n        renderer.drawTileMap(levelMap, 0, 0, Color::White);\n\n        // Draw entities (Scene::draw handles this)\n        Scene::draw(renderer);\n\n        // Reset offset for UI\n        renderer.setDisplayOffset(0, 0);\n        renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n    }\n};\n
    "},{"location":"api_reference/graphics/camera2d/#parallax-scrolling","title":"Parallax Scrolling","text":"

    Use multiple cameras or manual offset for parallax:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Background layer (slow parallax)\n    float bgOffsetX = camera.getX() * 0.5f;  // 50% speed\n    renderer.setDisplayOffset(-bgOffsetX, 0);\n    renderer.drawTileMap(backgroundMap, 0, 0, Color::White);\n\n    // Midground layer (normal speed)\n    camera.apply(renderer);\n    renderer.drawTileMap(midgroundMap, 0, 0, Color::White);\n\n    // Foreground (entities, normal speed)\n    Scene::draw(renderer);\n\n    // UI (no offset)\n    renderer.setDisplayOffset(0, 0);\n    renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n}\n
    "},{"location":"api_reference/graphics/camera2d/#performance-considerations","title":"Performance Considerations","text":"
    • Apply frequency: apply() is fast; safe to call every frame
    • Boundary checks: Boundary clamping is efficient
    • Following: Dead-zone calculations are lightweight
    "},{"location":"api_reference/graphics/camera2d/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Float math: Uses floating point; acceptable but integer math would be faster
    • Memory: Camera is small (few floats); minimal memory usage
    "},{"location":"api_reference/graphics/camera2d/#see-also","title":"See Also","text":"
    • Renderer - Rendering system
    • Manual - Cameras and Scrolling
    • API Overview
    "},{"location":"api_reference/graphics/color/","title":"Color","text":"

    Color constants and palette management system.

    "},{"location":"api_reference/graphics/color/#description","title":"Description","text":"

    The Color enum provides color constants that map to palette indices. The engine supports both legacy mode (single global palette) and dual palette mode (separate palettes for backgrounds and sprites).

    Colors are resolved to 16-bit RGB565 values based on the active palette(s).

    "},{"location":"api_reference/graphics/color/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    enum class Color : uint8_t {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/color/#color-enum-values","title":"Color Enum Values","text":""},{"location":"api_reference/graphics/color/#standard-colors-pr32-palette-indices","title":"Standard Colors (PR32 Palette Indices)","text":"
    • Color::Black (0)
    • Color::White (1)
    • Color::Navy (2)
    • Color::Blue (3)
    • Color::Cyan (4)
    • Color::DarkGreen (5)
    • Color::Green (6)
    • Color::LightGreen (7)
    • Color::Yellow (8)
    • Color::Orange (9)
    • Color::LightRed (10)
    • Color::Red (11)
    • Color::DarkRed (12)
    • Color::Purple (13)
    • Color::Magenta (14)
    • Color::Gray (15)
    "},{"location":"api_reference/graphics/color/#color-aliases","title":"Color Aliases","text":"

    For compatibility, several aliases map to the closest available color:

    • Color::DarkBlue \u2192 Navy
    • Color::LightBlue \u2192 Blue
    • Color::Teal \u2192 Cyan
    • Color::Olive \u2192 DarkGreen
    • Color::Gold \u2192 Yellow
    • Color::Brown \u2192 DarkRed
    • Color::Pink \u2192 Magenta
    • Color::LightPurple \u2192 Magenta
    • Color::Maroon \u2192 DarkRed
    • Color::MidGray \u2192 Gray
    • Color::LightGray \u2192 Gray
    • Color::DarkGray \u2192 Gray
    • Color::Silver \u2192 Gray
    "},{"location":"api_reference/graphics/color/#special-colors","title":"Special Colors","text":"
    • Color::Transparent (255): Not a real color; must be handled by renderer (results in no-op)
    • Color::DebugRed \u2192 Red
    • Color::DebugGreen \u2192 Green
    • Color::DebugBlue \u2192 Blue
    "},{"location":"api_reference/graphics/color/#palettetype-enum","title":"PaletteType Enum","text":"

    Built-in palette types:

    • PaletteType::NES: NES color palette
    • PaletteType::GB: Game Boy (4 shades of green)
    • PaletteType::GBC: Game Boy Color palette
    • PaletteType::PICO8: PICO-8 palette
    • PaletteType::PR32: PixelRoot32 default palette
    "},{"location":"api_reference/graphics/color/#palettecontext-enum","title":"PaletteContext Enum","text":"

    Context for palette selection in dual palette mode:

    • PaletteContext::Background: For backgrounds, tilemaps, and background primitives
    • PaletteContext::Sprite: For sprites, characters, and gameplay elements
    "},{"location":"api_reference/graphics/color/#palette-functions","title":"Palette Functions","text":""},{"location":"api_reference/graphics/color/#void-setpalettepalettetype-palette","title":"void setPalette(PaletteType palette)","text":"

    Selects the active color palette (legacy mode). Sets both background and sprite palettes to the same value.

    Parameters: - palette (PaletteType): The palette to use

    Notes: - Does not enable dual palette mode - All rendering uses the same palette - Use for simple games with single palette

    Example:

    pixelroot32::graphics::setPalette(pixelroot32::graphics::PaletteType::NES);\n

    "},{"location":"api_reference/graphics/color/#void-setcustompaletteconst-uint16_t-palette","title":"void setCustomPalette(const uint16_t* palette)","text":"

    Sets a custom color palette (legacy mode). Sets both background and sprite palettes to the same value.

    Parameters: - palette (const uint16_t*): Pointer to an array of 16 uint16_t RGB565 color values

    Notes: - Array must remain valid (use static/global storage) - Engine does not copy the palette - Does not enable dual palette mode

    Example:

    static const uint16_t MY_PALETTE[16] = {\n    0x0000,  // Black\n    0xFFFF,  // White\n    0x001F,  // Blue\n    // ... 13 more colors\n};\n\npixelroot32::graphics::setCustomPalette(MY_PALETTE);\n

    "},{"location":"api_reference/graphics/color/#void-enabledualpalettemodebool-enable","title":"void enableDualPaletteMode(bool enable)","text":"

    Enables or disables dual palette mode.

    Parameters: - enable (bool): true to enable dual palette mode, false for legacy mode

    Notes: - When enabled: backgrounds and sprites use separate palettes - When disabled: single palette for all rendering (legacy mode)

    Example:

    pixelroot32::graphics::enableDualPaletteMode(true);\n

    "},{"location":"api_reference/graphics/color/#void-setbackgroundpalettepalettetype-palette","title":"void setBackgroundPalette(PaletteType palette)","text":"

    Sets the background palette (for backgrounds, tilemaps, etc.).

    Parameters: - palette (PaletteType): The palette type to use for backgrounds

    Notes: - Only used in dual palette mode - Affects tilemaps, background primitives, etc.

    Example:

    pixelroot32::graphics::enableDualPaletteMode(true);\npixelroot32::graphics::setBackgroundPalette(pixelroot32::graphics::PaletteType::NES);\n

    "},{"location":"api_reference/graphics/color/#void-setspritepalettepalettetype-palette","title":"void setSpritePalette(PaletteType palette)","text":"

    Sets the sprite palette (for sprites, characters, etc.).

    Parameters: - palette (PaletteType): The palette type to use for sprites

    Notes: - Only used in dual palette mode - Affects sprites, characters, gameplay elements

    Example:

    pixelroot32::graphics::setSpritePalette(pixelroot32::graphics::PaletteType::GB);\n

    "},{"location":"api_reference/graphics/color/#void-setdualpalettepalettetype-bgpalette-palettetype-spritepalette","title":"void setDualPalette(PaletteType bgPalette, PaletteType spritePalette)","text":"

    Sets both background and sprite palettes at once. Automatically enables dual palette mode.

    Parameters: - bgPalette (PaletteType): The palette type to use for backgrounds - spritePalette (PaletteType): The palette type to use for sprites

    Example:

    pixelroot32::graphics::setDualPalette(\n    pixelroot32::graphics::PaletteType::NES,  // Background\n    pixelroot32::graphics::PaletteType::GB     // Sprites\n);\n

    "},{"location":"api_reference/graphics/color/#void-setbackgroundcustompaletteconst-uint16_t-palette","title":"void setBackgroundCustomPalette(const uint16_t* palette)","text":"

    Sets a custom background palette.

    Parameters: - palette (const uint16_t*): Pointer to an array of 16 uint16_t RGB565 color values

    Notes: - Array must remain valid (use static/global storage) - Only used in dual palette mode

    Example:

    static const uint16_t BG_PALETTE[16] = { /* ... */ };\npixelroot32::graphics::enableDualPaletteMode(true);\npixelroot32::graphics::setBackgroundCustomPalette(BG_PALETTE);\n

    "},{"location":"api_reference/graphics/color/#void-setspritecustompaletteconst-uint16_t-palette","title":"void setSpriteCustomPalette(const uint16_t* palette)","text":"

    Sets a custom sprite palette.

    Parameters: - palette (const uint16_t*): Pointer to an array of 16 uint16_t RGB565 color values

    Notes: - Array must remain valid (use static/global storage) - Only used in dual palette mode

    Example:

    static const uint16_t SPRITE_PALETTE[16] = { /* ... */ };\npixelroot32::graphics::setSpriteCustomPalette(SPRITE_PALETTE);\n

    "},{"location":"api_reference/graphics/color/#void-setdualcustompaletteconst-uint16_t-bgpalette-const-uint16_t-spritepal","title":"void setDualCustomPalette(const uint16_t bgPalette, const uint16_t spritePal)","text":"

    Sets both background and sprite custom palettes at once. Automatically enables dual palette mode.

    Parameters: - bgPalette (const uint16_t): Pointer to background palette array (16 RGB565 values) - spritePal (const uint16_t): Pointer to sprite palette array (16 RGB565 values)

    Example:

    static const uint16_t BG_PAL[16] = { /* ... */ };\nstatic const uint16_t SPRITE_PAL[16] = { /* ... */ };\npixelroot32::graphics::setDualCustomPalette(BG_PAL, SPRITE_PAL);\n

    "},{"location":"api_reference/graphics/color/#uint16_t-resolvecolorcolor-color","title":"uint16_t resolveColor(Color color)","text":"

    Resolves a Color enum to its corresponding 16-bit color value (legacy mode).

    Parameters: - color (Color): The Color enum value

    Returns: - uint16_t: The 16-bit RGB565 color value

    Notes: - Uses the current active palette (single palette mode) - Color::Transparent must not be resolved (handled by renderer) - Typically called internally by renderer

    Example:

    uint16_t rgb565 = pixelroot32::graphics::resolveColor(pixelroot32::graphics::Color::Red);\n

    "},{"location":"api_reference/graphics/color/#uint16_t-resolvecolorcolor-color-palettecontext-context","title":"uint16_t resolveColor(Color color, PaletteContext context)","text":"

    Resolves a Color enum with context (dual palette mode).

    Parameters: - color (Color): The Color enum value - context (PaletteContext): The palette context (Background or Sprite)

    Returns: - uint16_t: The 16-bit RGB565 color value

    Notes: - Uses the appropriate palette based on context - In legacy mode, context is ignored - Color::Transparent must not be resolved

    Example:

    uint16_t bgColor = pixelroot32::graphics::resolveColor(\n    pixelroot32::graphics::Color::Blue,\n    pixelroot32::graphics::PaletteContext::Background\n);\n

    "},{"location":"api_reference/graphics/color/#rgb565-format","title":"RGB565 Format","text":"

    Colors are stored as 16-bit RGB565 values:

    • Bits 15-11: Red (5 bits, 0-31)
    • Bits 10-5: Green (6 bits, 0-63)
    • Bits 4-0: Blue (5 bits, 0-31)

    Conversion Example:

    // Convert RGB to RGB565\nuint16_t rgb565 = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);\n

    "},{"location":"api_reference/graphics/color/#usage-examples","title":"Usage Examples","text":""},{"location":"api_reference/graphics/color/#legacy-mode-single-palette","title":"Legacy Mode (Single Palette)","text":"
    // Set single palette for all rendering\npixelroot32::graphics::setPalette(pixelroot32::graphics::PaletteType::NES);\n\n// Use colors\nrenderer.drawSprite(sprite, 100, 100, pixelroot32::graphics::Color::Red);\nrenderer.drawFilledRectangle(10, 10, 50, 50, pixelroot32::graphics::Color::Blue);\n
    "},{"location":"api_reference/graphics/color/#dual-palette-mode","title":"Dual Palette Mode","text":"
    // Enable dual palette mode\npixelroot32::graphics::enableDualPaletteMode(true);\n\n// Set different palettes for backgrounds and sprites\npixelroot32::graphics::setBackgroundPalette(pixelroot32::graphics::PaletteType::NES);\npixelroot32::graphics::setSpritePalette(pixelroot32::graphics::PaletteType::GB);\n\n// Or use convenience function\npixelroot32::graphics::setDualPalette(\n    pixelroot32::graphics::PaletteType::NES,\n    pixelroot32::graphics::PaletteType::GB\n);\n
    "},{"location":"api_reference/graphics/color/#custom-palettes","title":"Custom Palettes","text":"
    // Define custom palette (RGB565 values)\nstatic const uint16_t CUSTOM_PALETTE[16] = {\n    0x0000,  // 0: Black\n    0xFFFF,  // 1: White\n    0x001F,  // 2: Blue\n    0x07E0,  // 3: Green\n    0xF800,  // 4: Red\n    // ... 11 more colors\n};\n\n// Use in legacy mode\npixelroot32::graphics::setCustomPalette(CUSTOM_PALETTE);\n\n// Or use in dual palette mode\npixelroot32::graphics::enableDualPaletteMode(true);\npixelroot32::graphics::setBackgroundCustomPalette(CUSTOM_PALETTE);\npixelroot32::graphics::setSpriteCustomPalette(CUSTOM_PALETTE);\n
    "},{"location":"api_reference/graphics/color/#performance-considerations","title":"Performance Considerations","text":"
    • Color resolution: Fast lookup operation
    • Palette switching: Changing palettes is fast (just pointer assignment)
    • Memory: Palettes are stored in flash (const arrays) for best performance
    • Dual mode: Slightly more overhead than legacy mode, but minimal
    "},{"location":"api_reference/graphics/color/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Flash storage: Store custom palettes in flash (const/constexpr)
    • Memory: Palettes are small (16 uint16_t = 32 bytes)
    • Palette switching: Avoid switching palettes every frame
    "},{"location":"api_reference/graphics/color/#see-also","title":"See Also","text":"
    • Renderer - Rendering system
    • Manual - Color Palettes
    • API Overview
    "},{"location":"api_reference/graphics/display_config/","title":"DisplayConfig","text":"

    Configuration settings for initializing the display.

    "},{"location":"api_reference/graphics/display_config/#description","title":"Description","text":"

    DisplayConfig holds display parameters used by the renderer and camera to draw correctly on the target device. It defines the display type, dimensions, rotation, and creates the appropriate DrawSurface implementation for the platform.

    "},{"location":"api_reference/graphics/display_config/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    struct DisplayConfig {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/display_config/#displaytype-enum","title":"DisplayType Enum","text":"

    Supported display types:

    • DisplayType::ST7789: 240x240 TFT display
    • DisplayType::ST7735: 128x128 TFT display
    • DisplayType::NONE: For SDL2 native (no driver needed)
    "},{"location":"api_reference/graphics/display_config/#structure","title":"Structure","text":""},{"location":"api_reference/graphics/display_config/#displaytype-type","title":"DisplayType type","text":"

    The type of display.

    Type: DisplayType enum

    Access: Read-write

    Notes: - Determines which driver to use (ESP32) - NONE for Native/SDL2 platform

    "},{"location":"api_reference/graphics/display_config/#int-rotation","title":"int rotation","text":"

    Display rotation in degrees.

    Type: int

    Access: Read-write

    Default: 0

    Notes: - Common values: 0, 90, 180, 270 - Rotation is applied during initialization - Some displays may not support all rotations

    "},{"location":"api_reference/graphics/display_config/#uint16_t-width","title":"uint16_t width","text":"

    Display width in pixels.

    Type: uint16_t

    Access: Read-write

    Default: 240

    Notes: - Should match actual display width - Used for viewport and camera calculations

    "},{"location":"api_reference/graphics/display_config/#uint16_t-height","title":"uint16_t height","text":"

    Display height in pixels.

    Type: uint16_t

    Access: Read-write

    Default: 240

    Notes: - Should match actual display height - Used for viewport and camera calculations

    "},{"location":"api_reference/graphics/display_config/#int-xoffset","title":"int xOffset","text":"

    X offset for display alignment.

    Type: int

    Access: Read-write

    Default: 0

    Notes: - Used to adjust display position - Some displays need offset for proper alignment

    "},{"location":"api_reference/graphics/display_config/#int-yoffset","title":"int yOffset","text":"

    Y offset for display alignment.

    Type: int

    Access: Read-write

    Default: 0

    Notes: - Used to adjust display position - Some displays need offset for proper alignment

    "},{"location":"api_reference/graphics/display_config/#drawsurface-getdrawsurface-const","title":"DrawSurface& getDrawSurface() const","text":"

    Gets the underlying DrawSurface implementation.

    Returns: - DrawSurface&: Reference to the DrawSurface

    Notes: - Advanced usage: typically not needed - Provides access to low-level display driver - Platform-specific implementation

    "},{"location":"api_reference/graphics/display_config/#constructors","title":"Constructors","text":""},{"location":"api_reference/graphics/display_config/#displayconfigdisplaytype-type-const-int-rot-0-uint16_t-w-240-uint16_t-h-240-const-int-xoffset-0-const-int-yoffset-0","title":"DisplayConfig(DisplayType type, const int rot = 0, uint16_t w = 240, uint16_t h = 240, const int xOffset = 0, const int yOffset = 0)","text":"

    Constructs a DisplayConfig with specified parameters.

    Parameters: - type (DisplayType): The display type - rot (int, optional): Rotation in degrees. Default: 0 - w (uint16_t, optional): Width in pixels. Default: 240 - h (uint16_t, optional): Height in pixels. Default: 240 - xOffset (int, optional): X offset. Default: 0 - yOffset (int, optional): Y offset. Default: 0

    Notes: - Automatically creates the appropriate DrawSurface for the platform - ESP32: Creates TFT_eSPI_Drawer based on display type - Native: Creates SDL2_Drawer

    Example:

    // ST7789 display, 240x240, no rotation\npixelroot32::graphics::DisplayConfig config(\n    pixelroot32::graphics::DisplayType::ST7789\n);\n\n// ST7735 display, 128x128, rotated 90 degrees\npixelroot32::graphics::DisplayConfig config(\n    pixelroot32::graphics::DisplayType::ST7735,\n    90,   // rotation\n    128,  // width\n    128   // height\n);\n\n// Native/SDL2 (no specific display type)\npixelroot32::graphics::DisplayConfig config(\n    pixelroot32::graphics::DisplayType::NONE,\n    0,    // rotation\n    128,  // width\n    128   // height\n);\n

    "},{"location":"api_reference/graphics/display_config/#usage-examples","title":"Usage Examples","text":""},{"location":"api_reference/graphics/display_config/#esp32-with-st7789","title":"ESP32 with ST7789","text":"
    #ifdef PLATFORM_ESP32\n#include \"graphics/DisplayConfig.h\"\n\nvoid setup() {\n    // Configure ST7789 display (240x240)\n    pixelroot32::graphics::DisplayConfig displayConfig(\n        pixelroot32::graphics::DisplayType::ST7789,\n        0,    // rotation\n        240,  // width\n        240   // height\n    );\n\n    // Use with Engine\n    pixelroot32::core::Engine engine(displayConfig);\n    engine.init();\n    engine.run();\n}\n#endif\n
    "},{"location":"api_reference/graphics/display_config/#esp32-with-st7735","title":"ESP32 with ST7735","text":"
    #ifdef PLATFORM_ESP32\n    // Configure ST7735 display (128x128)\n    pixelroot32::graphics::DisplayConfig displayConfig(\n        pixelroot32::graphics::DisplayType::ST7735,\n        0,    // rotation\n        128,  // width\n        128   // height\n    );\n#endif\n
    "},{"location":"api_reference/graphics/display_config/#nativesdl2","title":"Native/SDL2","text":"
    #ifdef PLATFORM_NATIVE\n    // Native display (SDL2 window)\n    pixelroot32::graphics::DisplayConfig displayConfig(\n        pixelroot32::graphics::DisplayType::NONE,\n        0,    // rotation\n        128,  // width\n        128   // height\n    );\n#endif\n
    "},{"location":"api_reference/graphics/display_config/#platform-agnostic-setup","title":"Platform-Agnostic Setup","text":"
    #include \"graphics/DisplayConfig.h\"\n#include \"core/Engine.h\"\n\nvoid setup() {\n    pixelroot32::graphics::DisplayConfig displayConfig;\n\n    #ifdef PLATFORM_ESP32\n        displayConfig.type = pixelroot32::graphics::DisplayType::ST7789;\n        displayConfig.width = 240;\n        displayConfig.height = 240;\n    #elif PLATFORM_NATIVE\n        displayConfig.type = pixelroot32::graphics::DisplayType::NONE;\n        displayConfig.width = 128;\n        displayConfig.height = 128;\n    #endif\n\n    displayConfig.rotation = 0;\n\n    pixelroot32::core::Engine engine(displayConfig);\n    engine.init();\n    engine.run();\n}\n
    "},{"location":"api_reference/graphics/display_config/#display-type-details","title":"Display Type Details","text":""},{"location":"api_reference/graphics/display_config/#st7789","title":"ST7789","text":"
    • Resolution: Typically 240x240 or 240x320
    • Interface: SPI
    • Driver: TFT_eSPI
    • Common sizes: 240x240, 240x320
    "},{"location":"api_reference/graphics/display_config/#st7735","title":"ST7735","text":"
    • Resolution: Typically 128x128 or 128x160
    • Interface: SPI
    • Driver: TFT_eSPI
    • Common sizes: 128x128, 128x160
    "},{"location":"api_reference/graphics/display_config/#none-native","title":"NONE (Native)","text":"
    • Platform: Native/SDL2
    • Driver: SDL2_Drawer
    • Resolution: Configurable (any size)
    • Window: Creates SDL2 window
    "},{"location":"api_reference/graphics/display_config/#rotation","title":"Rotation","text":"

    Display rotation values:

    • 0: Normal orientation
    • 90: Rotated 90 degrees clockwise
    • 180: Rotated 180 degrees
    • 270: Rotated 90 degrees counter-clockwise

    Notes: - Rotation affects coordinate system - Some displays may not support all rotations - Test rotation on your specific hardware

    "},{"location":"api_reference/graphics/display_config/#performance-considerations","title":"Performance Considerations","text":"
    • Initialization: Display initialization happens once at startup
    • Driver selection: Automatic based on platform and type
    • Memory: DisplayConfig is small (few fields)
    "},{"location":"api_reference/graphics/display_config/#esp32-considerations","title":"ESP32 Considerations","text":""},{"location":"api_reference/graphics/display_config/#tft_espi-configuration","title":"TFT_eSPI Configuration","text":"

    DisplayConfig uses TFT_eSPI driver. Additional configuration may be needed in platformio.ini:

    build_flags =\n    -DUSER_SETUP_LOADED=1\n    -DST7789_DRIVER=1\n    -DTFT_WIDTH=240\n    -DTFT_HEIGHT=240\n    # ... pin configuration\n
    "},{"location":"api_reference/graphics/display_config/#pin-configuration","title":"Pin Configuration","text":"

    GPIO pins must be configured separately (not in DisplayConfig):

    • MOSI: Data pin
    • SCLK: Clock pin
    • DC: Data/Command pin
    • RST: Reset pin
    • CS: Chip select pin (optional)
    "},{"location":"api_reference/graphics/display_config/#see-also","title":"See Also","text":"
    • Renderer - Rendering system
    • Camera2D - Camera that uses display dimensions
    • Manual - Platforms and Drivers
    • API Overview
    "},{"location":"api_reference/graphics/font/","title":"Font","text":"

    Descriptor for a bitmap font using 1bpp sprites.

    "},{"location":"api_reference/graphics/font/#description","title":"Description","text":"

    A Font contains an array of Sprite structures, one for each character in the font's character set. Each glyph is rendered as a 1bpp sprite, allowing consistent rendering across platforms.

    The font uses fixed-width glyphs for simplicity and performance. All glyphs share the same width and height, with spacing between characters controlled by the spacing field.

    "},{"location":"api_reference/graphics/font/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    struct Font {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/font/#structure","title":"Structure","text":""},{"location":"api_reference/graphics/font/#const-sprite-glyphs","title":"const Sprite* glyphs","text":"

    Array of sprites, one per character (indexed by character code - firstChar).

    Type: const Sprite*

    Access: Read-only

    Notes: - Array must contain sprites for all characters from firstChar to lastChar - Each sprite represents one character glyph - Should be stored in flash (const/constexpr) for best performance

    Example:

    static const Sprite FONT_GLYPHS[] = {\n    spaceGlyph,    // Index 0 = ' ' (firstChar = 32)\n    exclamationGlyph, // Index 1 = '!'\n    // ... more glyphs\n};\n\nstatic const Font myFont = {\n    FONT_GLYPHS,\n    32,  // firstChar\n    126, // lastChar\n    5,   // glyphWidth\n    7,   // glyphHeight\n    1,   // spacing\n    8    // lineHeight\n};\n

    "},{"location":"api_reference/graphics/font/#uint8_t-firstchar","title":"uint8_t firstChar","text":"

    First character code in the font.

    Type: uint8_t

    Access: Read-only

    Default: Typically 32 (space character)

    Notes: - ASCII code of the first character - Common: 32 (space ' ') for ASCII fonts - Glyphs array starts at index 0 for this character

    Example:

    firstChar = 32;  // Starts at space character\n

    "},{"location":"api_reference/graphics/font/#uint8_t-lastchar","title":"uint8_t lastChar","text":"

    Last character code in the font.

    Type: uint8_t

    Access: Read-only

    Default: Typically 126 (tilde '~')

    Notes: - ASCII code of the last character - Common: 126 (tilde '~') for full ASCII fonts - Glyphs array must contain (lastChar - firstChar + 1) sprites

    Example:

    lastChar = 126;  // Ends at tilde character\n// Font contains characters 32-126 (95 characters)\n

    "},{"location":"api_reference/graphics/font/#uint8_t-glyphwidth","title":"uint8_t glyphWidth","text":"

    Fixed width of each glyph in pixels.

    Type: uint8_t

    Access: Read-only

    Notes: - All glyphs must have the same width - Typical values: 5, 6, 8 pixels - Smaller = more characters per line, less readable

    Example:

    glyphWidth = 5;  // 5 pixels wide (like FONT_5X7)\n

    "},{"location":"api_reference/graphics/font/#uint8_t-glyphheight","title":"uint8_t glyphHeight","text":"

    Fixed height of each glyph in pixels.

    Type: uint8_t

    Access: Read-only

    Notes: - All glyphs must have the same height - Typical values: 7, 8, 10 pixels - Smaller = more lines, less readable

    Example:

    glyphHeight = 7;  // 7 pixels tall (like FONT_5X7)\n

    "},{"location":"api_reference/graphics/font/#uint8_t-spacing","title":"uint8_t spacing","text":"

    Horizontal spacing between characters in pixels.

    Type: uint8_t

    Access: Read-only

    Default: Typically 1

    Notes: - Space added between characters - 0 = no spacing (characters touch) - 1 = 1 pixel gap (common) - Higher values = more readable but wider text

    Example:

    spacing = 1;  // 1 pixel between characters\n

    "},{"location":"api_reference/graphics/font/#uint8_t-lineheight","title":"uint8_t lineHeight","text":"

    Total line height including vertical spacing.

    Type: uint8_t

    Access: Read-only

    Notes: - Should be glyphHeight + verticalSpacing - Used for line breaks and multi-line text - Typical: glyphHeight + 1 or glyphHeight + 2

    Example:

    lineHeight = 8;  // 7 pixel glyph + 1 pixel spacing\n

    "},{"location":"api_reference/graphics/font/#built-in-fonts","title":"Built-in Fonts","text":""},{"location":"api_reference/graphics/font/#font_5x7","title":"FONT_5X7","text":"

    Standard 5x7 pixel font (built-in).

    Properties: - Width: 5 pixels - Height: 7 pixels - Characters: Typically ASCII 32-126 - Spacing: 1 pixel - Line height: 8 pixels

    Usage:

    #include \"graphics/Font.h\"\n\nrenderer.drawText(\"Hello\", 10, 10, Color::White, 1, &FONT_5X7);\n

    "},{"location":"api_reference/graphics/font/#creating-custom-fonts","title":"Creating Custom Fonts","text":""},{"location":"api_reference/graphics/font/#step-1-create-glyph-sprites","title":"Step 1: Create Glyph Sprites","text":"
    // Space character (ASCII 32)\nstatic const uint16_t SPACE_GLYPH[] = {\n    0b00000,\n    0b00000,\n    0b00000,\n    0b00000,\n    0b00000,\n    0b00000,\n    0b00000\n};\n\n// 'A' character (ASCII 65)\nstatic const uint16_t A_GLYPH[] = {\n    0b00100,\n    0b01010,\n    0b10001,\n    0b11111,\n    0b10001,\n    0b10001,\n    0b00000\n};\n\n// ... more glyphs\n
    "},{"location":"api_reference/graphics/font/#step-2-create-glyph-array","title":"Step 2: Create Glyph Array","text":"
    static const Sprite FONT_GLYPHS[] = {\n    {SPACE_GLYPH, 5, 7},  // Index 0 = ' ' (32)\n    // ... more glyphs\n    {A_GLYPH, 5, 7},      // Index 33 = 'A' (65)\n    // ... more glyphs\n};\n
    "},{"location":"api_reference/graphics/font/#step-3-create-font-structure","title":"Step 3: Create Font Structure","text":"
    static const Font MY_FONT = {\n    FONT_GLYPHS,  // glyphs array\n    32,           // firstChar (space)\n    126,          // lastChar (tilde)\n    5,            // glyphWidth\n    7,            // glyphHeight\n    1,            // spacing\n    8             // lineHeight\n};\n
    "},{"location":"api_reference/graphics/font/#step-4-use-font","title":"Step 4: Use Font","text":"
    renderer.drawText(\"Hello\", 10, 10, Color::White, 1, &MY_FONT);\n
    "},{"location":"api_reference/graphics/font/#usage-example","title":"Usage Example","text":"
    #include \"graphics/Font.h\"\n#include \"graphics/Renderer.h\"\n\n// Using built-in font\nvoid draw(Renderer& renderer) override {\n    // Default font\n    renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n\n    // Explicit font\n    renderer.drawText(\"Score: 100\", 10, 30, Color::White, 1, &FONT_5X7);\n\n    // Centered text with font\n    renderer.drawTextCentered(\"Game Over\", 64, Color::Yellow, 2, &FONT_5X7);\n}\n\n// Custom font example\nstatic const uint16_t CUSTOM_GLYPHS[][7] = {\n    // Space, A, B, C, etc.\n};\n\nstatic const Sprite CUSTOM_SPRITES[] = {\n    {CUSTOM_GLYPHS[0], 6, 8},  // Space\n    {CUSTOM_GLYPHS[1], 6, 8},  // A\n    // ... more\n};\n\nstatic const Font CUSTOM_FONT = {\n    CUSTOM_SPRITES,\n    32,   // firstChar\n    90,   // lastChar (A-Z only)\n    6,    // width\n    8,    // height\n    1,    // spacing\n    9     // lineHeight\n};\n\nvoid draw(Renderer& renderer) override {\n    renderer.drawText(\"CUSTOM\", 10, 10, Color::White, 1, &CUSTOM_FONT);\n}\n
    "},{"location":"api_reference/graphics/font/#text-sizing","title":"Text Sizing","text":"

    Calculate text dimensions:

    int getTextWidth(const Font* font, const char* text) {\n    if (!font || !text) return 0;\n\n    int width = 0;\n    for (int i = 0; text[i] != '\\0'; i++) {\n        if (text[i] >= font->firstChar && text[i] <= font->lastChar) {\n            width += font->glyphWidth + font->spacing;\n        }\n    }\n    return width - font->spacing;  // Remove last spacing\n}\n\nint getTextHeight(const Font* font) {\n    return font ? font->lineHeight : 8;\n}\n
    "},{"location":"api_reference/graphics/font/#performance-considerations","title":"Performance Considerations","text":"
    • Font storage: Store fonts in flash (const/constexpr) for best performance
    • Glyph lookup: Fast array access (character code - firstChar)
    • Fixed width: Fixed-width fonts are faster than variable-width
    • Font switching: Changing fonts is fast (just pointer assignment)
    "},{"location":"api_reference/graphics/font/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Store font data in flash, not RAM
    • Font size: Larger fonts use more flash memory
    • Character range: Limit character range to save memory if not needed
    "},{"location":"api_reference/graphics/font/#see-also","title":"See Also","text":"
    • Renderer - Rendering system that uses fonts
    • Sprite - Sprite structure used for glyphs
    • Manual - Basic Rendering
    • API Overview
    "},{"location":"api_reference/graphics/renderer/","title":"Renderer","text":"

    High-level graphics rendering system for drawing shapes, text, sprites, and tilemaps.

    "},{"location":"api_reference/graphics/renderer/#description","title":"Description","text":"

    The Renderer class provides a unified API for drawing shapes, text, and images. It abstracts the underlying hardware implementation (DrawSurface) and manages display configuration, including rotation and offsets.

    The renderer uses integer-only math for optimal performance on ESP32 and supports multiple sprite formats (1bpp, 2bpp, 4bpp) and multi-layer sprites.

    "},{"location":"api_reference/graphics/renderer/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    class Renderer {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/renderer/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Engine (manages renderer instance)
    "},{"location":"api_reference/graphics/renderer/#constructors","title":"Constructors","text":""},{"location":"api_reference/graphics/renderer/#rendererconst-displayconfig-config","title":"Renderer(const DisplayConfig& config)","text":"

    Constructs the Renderer with a specific display configuration.

    Parameters: - config (const DisplayConfig&): The display configuration settings (width, height, rotation, etc.)

    Example:

    #include \"graphics/Renderer.h\"\n#include \"graphics/DisplayConfig.h\"\n\npixelroot32::graphics::DisplayConfig config;\nconfig.width = 128;\nconfig.height = 128;\nconfig.rotation = 0;\n\npixelroot32::graphics::Renderer renderer(config);\nrenderer.init();\n

    "},{"location":"api_reference/graphics/renderer/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/graphics/renderer/#void-init","title":"void init()","text":"

    Initializes the renderer and the underlying draw surface.

    Returns: - void

    Notes: - Must be called after construction and before any drawing operations - Initializes the platform-specific DrawSurface implementation - Safe to call multiple times (idempotent)

    Example:

    Renderer renderer(displayConfig);\nrenderer.init();  // Initialize before use\n

    "},{"location":"api_reference/graphics/renderer/#void-beginframe","title":"void beginFrame()","text":"

    Prepares the buffer for a new frame (clears screen).

    Returns: - void

    Notes: - Should be called once at the start of each frame - Clears the display buffer - Typically called automatically by Engine, but can be called manually

    Example:

    void draw(Renderer& renderer) override {\n    renderer.beginFrame();\n    // Draw everything...\n    renderer.endFrame();\n}\n

    "},{"location":"api_reference/graphics/renderer/#void-endframe","title":"void endFrame()","text":"

    Finalizes the frame and sends the buffer to the display.

    Returns: - void

    Notes: - Should be called once at the end of each frame - Sends the completed frame buffer to the display - Typically called automatically by Engine, but can be called manually

    "},{"location":"api_reference/graphics/renderer/#drawsurface-getdrawsurface","title":"DrawSurface& getDrawSurface()","text":"

    Gets the underlying DrawSurface implementation.

    Returns: - DrawSurface&: Reference to the DrawSurface

    Notes: - Advanced usage: typically not needed unless implementing custom drawing - Provides low-level access to the display driver

    "},{"location":"api_reference/graphics/renderer/#void-drawtextconst-char-text-int16_t-x-int16_t-y-color-color-uint8_t-size","title":"void drawText(const char* text, int16_t x, int16_t y, Color color, uint8_t size)","text":"

    Draws a string of text using the default font.

    Parameters: - text (const char*): The text to draw (null-terminated string) - x (int16_t): X coordinate (top-left corner of text) - y (int16_t): Y coordinate (top-left corner of text) - color (Color): Text color - size (uint8_t): Text size multiplier (1 = normal, 2 = double, etc.)

    Performance Notes: - Efficient for small amounts of text - Avoid calling in tight loops with long strings - Use static buffers for text formatting

    Example:

    renderer.drawText(\"Hello World\", 10, 10, Color::White, 1);\nrenderer.drawText(\"Score: 100\", 10, 30, Color::Yellow, 2);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawtextconst-char-text-int16_t-x-int16_t-y-color-color-uint8_t-size-const-font-font","title":"void drawText(const char text, int16_t x, int16_t y, Color color, uint8_t size, const Font font)","text":"

    Draws a string of text using a specific font.

    Parameters: - text (const char): The text to draw - x (int16_t): X coordinate - y (int16_t): Y coordinate - color (Color): Text color - size (uint8_t): Text size multiplier - font (const Font): Pointer to the font to use. If nullptr, uses the default font

    Example:

    const Font* customFont = &FONT_5X7;\nrenderer.drawText(\"Custom Font\", 10, 10, Color::White, 1, customFont);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawtextcenteredconst-char-text-int16_t-y-color-color-uint8_t-size","title":"void drawTextCentered(const char* text, int16_t y, Color color, uint8_t size)","text":"

    Draws text centered horizontally at a given Y coordinate using the default font.

    Parameters: - text (const char*): The text to draw - y (int16_t): Y coordinate - color (Color): Text color - size (uint8_t): Text size

    Example:

    renderer.drawTextCentered(\"Game Over\", 64, Color::White, 2);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawtextcenteredconst-char-text-int16_t-y-color-color-uint8_t-size-const-font-font","title":"void drawTextCentered(const char text, int16_t y, Color color, uint8_t size, const Font font)","text":"

    Draws text centered horizontally at a given Y coordinate using a specific font.

    Parameters: - text (const char): The text to draw - y (int16_t): Y coordinate - color (Color): Text color - size (uint8_t): Text size - font (const Font): Pointer to the font to use. If nullptr, uses the default font

    "},{"location":"api_reference/graphics/renderer/#void-drawfilledcircleint-x-int-y-int-radius-color-color","title":"void drawFilledCircle(int x, int y, int radius, Color color)","text":"

    Draws a filled circle.

    Parameters: - x (int): Center X coordinate - y (int): Center Y coordinate - radius (int): Radius of the circle in pixels - color (Color): Fill color

    Example:

    renderer.drawFilledCircle(64, 64, 20, Color::Red);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawcircleint-x-int-y-int-radius-color-color","title":"void drawCircle(int x, int y, int radius, Color color)","text":"

    Draws a circle outline.

    Parameters: - x (int): Center X coordinate - y (int): Center Y coordinate - radius (int): Radius of the circle in pixels - color (Color): Outline color

    Example:

    renderer.drawCircle(64, 64, 20, Color::White);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawrectangleint-x-int-y-int-width-int-height-color-color","title":"void drawRectangle(int x, int y, int width, int height, Color color)","text":"

    Draws a rectangle outline.

    Parameters: - x (int): Top-left X coordinate - y (int): Top-left Y coordinate - width (int): Width of the rectangle in pixels - height (int): Height of the rectangle in pixels - color (Color): Outline color

    Example:

    renderer.drawRectangle(10, 10, 100, 50, Color::Blue);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawfilledrectangleint-x-int-y-int-width-int-height-color-color","title":"void drawFilledRectangle(int x, int y, int width, int height, Color color)","text":"

    Draws a filled rectangle.

    Parameters: - x (int): Top-left X coordinate - y (int): Top-left Y coordinate - width (int): Width of the rectangle in pixels - height (int): Height of the rectangle in pixels - color (Color): Fill color

    Example:

    renderer.drawFilledRectangle(10, 10, 100, 50, Color::Green);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawlineint-x1-int-y1-int-x2-int-y2-color-color","title":"void drawLine(int x1, int y1, int x2, int y2, Color color)","text":"

    Draws a line between two points.

    Parameters: - x1 (int): Start X coordinate - y1 (int): Start Y coordinate - x2 (int): End X coordinate - y2 (int): End Y coordinate - color (Color): Line color

    Example:

    renderer.drawLine(0, 0, 128, 128, Color::White);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawpixelint-x-int-y-color-color","title":"void drawPixel(int x, int y, Color color)","text":"

    Draws a single pixel.

    Parameters: - x (int): X coordinate - y (int): Y coordinate - color (Color): Pixel color

    Performance Notes: - Very fast, but avoid calling thousands of times per frame - Use for special effects or debugging

    Example:

    renderer.drawPixel(64, 64, Color::Red);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawspriteconst-sprite-sprite-int-x-int-y-color-color-bool-flipx-false","title":"void drawSprite(const Sprite& sprite, int x, int y, Color color, bool flipX = false)","text":"

    Draws a 1bpp monochrome sprite using the Sprite descriptor.

    Parameters: - sprite (const Sprite&): Sprite descriptor (data, width, height) - x (int): Top-left X coordinate in logical screen space - y (int): Top-left Y coordinate in logical screen space - color (Color): Color used for \"on\" pixels. Default: uses sprite palette context - flipX (bool, optional): If true, sprite is mirrored horizontally. Default: false

    Performance Notes: - Very efficient for 1bpp sprites (integer-only operations) - Sprite data should be stored in flash (const/constexpr) for best performance - Avoid calling in tight loops; batch similar operations when possible

    Example:

    static const uint16_t playerData[] = {\n    0b00111100,\n    0b01111110,\n    // ... more rows\n};\n\nstatic const Sprite playerSprite = {\n    playerData,\n    8,  // width\n    8   // height\n};\n\nrenderer.drawSprite(playerSprite, 100, 100, Color::White);\nrenderer.drawSprite(playerSprite, 120, 100, Color::White, true);  // Flipped\n

    "},{"location":"api_reference/graphics/renderer/#void-drawspriteconst-sprite-sprite-int-x-int-y-float-scalex-float-scaley-color-color-bool-flipx-false","title":"void drawSprite(const Sprite& sprite, int x, int y, float scaleX, float scaleY, Color color, bool flipX = false)","text":"

    Draws a scaled 1bpp monochrome sprite.

    Parameters: - sprite (const Sprite&): Sprite descriptor - x (int): Top-left X coordinate - y (int): Top-left Y coordinate - scaleX (float): Horizontal scaling factor (e.g., 1.25 for 25% larger) - scaleY (float): Vertical scaling factor - color (Color): Color used for \"on\" pixels - flipX (bool, optional): If true, sprite is mirrored horizontally before scaling. Default: false

    Performance Notes: - Slower than non-scaled version due to scaling calculations - Use integer scaling factors when possible (1.0, 2.0, etc.) for better performance

    Example:

    renderer.drawSprite(playerSprite, 100, 100, 2.0f, 2.0f, Color::White);  // 2x size\n

    "},{"location":"api_reference/graphics/renderer/#void-drawmultispriteconst-multisprite-sprite-int-x-int-y","title":"void drawMultiSprite(const MultiSprite& sprite, int x, int y)","text":"

    Draws a multi-layer sprite composed of several 1bpp layers.

    Parameters: - sprite (const MultiSprite&): Multi-layer sprite descriptor - x (int): Top-left X coordinate in logical screen space - y (int): Top-left Y coordinate in logical screen space

    Performance Notes: - Each layer is rendered separately, so more layers = more draw calls - Still efficient as each layer uses 1bpp format - Use for multi-color sprites without higher bit-depths

    Example:

    static const SpriteLayer layers[] = {\n    { outlineData, Color::Black },\n    { fillData, Color::Red },\n    { highlightData, Color::Yellow }\n};\n\nstatic const MultiSprite playerMultiSprite = {\n    8,      // width\n    8,      // height\n    layers, // layers array\n    3       // layer count\n};\n\nrenderer.drawMultiSprite(playerMultiSprite, 100, 100);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawmultispriteconst-multisprite-sprite-int-x-int-y-float-scalex-float-scaley","title":"void drawMultiSprite(const MultiSprite& sprite, int x, int y, float scaleX, float scaleY)","text":"

    Draws a scaled multi-layer sprite.

    Parameters: - sprite (const MultiSprite&): Multi-layer sprite descriptor - x (int): Top-left X coordinate - y (int): Top-left Y coordinate - scaleX (float): Horizontal scaling factor - scaleY (float): Vertical scaling factor

    "},{"location":"api_reference/graphics/renderer/#void-drawtilemapconst-tilemap-map-int-originx-int-originy-color-color","title":"void drawTileMap(const TileMap& map, int originX, int originY, Color color)","text":"

    Draws a 1bpp tilemap.

    Parameters: - map (const TileMap&): Tilemap descriptor (indices, 1bpp tiles, dimensions) - originX (int): X coordinate of the top-left corner - originY (int): Y coordinate of the top-left corner - color (Color): Color used for all tiles in the map

    "},{"location":"api_reference/graphics/renderer/#void-drawtilemapconst-tilemap2bpp-map-int-originx-int-originy","title":"void drawTileMap(const TileMap2bpp& map, int originX, int originY)","text":"

    Draws a 2bpp tilemap. Available when PIXELROOT32_ENABLE_2BPP_SPRITES is defined.

    Parameters: - map (const TileMap2bpp&): Tilemap descriptor (indices, 2bpp tiles, dimensions) - originX (int): X coordinate - originY (int): Y coordinate

    "},{"location":"api_reference/graphics/renderer/#void-drawtilemapconst-tilemap4bpp-map-int-originx-int-originy","title":"void drawTileMap(const TileMap4bpp& map, int originX, int originY)","text":"

    Draws a 4bpp tilemap. Available when PIXELROOT32_ENABLE_4BPP_SPRITES is defined.

    Parameters: - map (const TileMap4bpp&): Tilemap descriptor (indices, 4bpp tiles, dimensions) - originX (int): X coordinate - originY (int): Y coordinate

    Performance Notes: - Very efficient for rendering large backgrounds - Only visible tiles are drawn (viewport culling) - Use tilemaps instead of individual sprites for backgrounds

    Example:

    static const uint8_t levelIndices[] = {\n    0, 1, 2, 3,\n    4, 5, 6, 7,\n    // ... more rows\n};\n\nstatic const TileMap levelMap = {\n    levelIndices,\n    16,        // width in tiles\n    16,        // height in tiles\n    tileSprites, // tile sprite array\n    8,         // tile width\n    8,         // tile height\n    16         // tile count\n};\n\nrenderer.drawTileMap(levelMap, 0, 0, Color::White);\n

    "},{"location":"api_reference/graphics/renderer/#void-setdisplayoffsetint-x-int-y","title":"void setDisplayOffset(int x, int y)","text":"

    Sets a global offset for all drawing operations. Useful for camera/parallax effects.

    Parameters: - x (int): X offset in pixels - y (int): Y offset in pixels

    Notes: - All subsequent drawing operations are offset by this amount - Useful for camera scrolling and parallax effects - Reset to (0, 0) to disable offset

    Example:

    // Camera scrolling\ncamera.setPosition(playerX - 64, playerY - 64);\nrenderer.setDisplayOffset(-camera.getX(), -camera.getY());\nrenderer.drawTileMap(background, 0, 0, Color::White);\n

    "},{"location":"api_reference/graphics/renderer/#void-setdisplaysizeint-w-int-h","title":"void setDisplaySize(int w, int h)","text":"

    Sets the logical display size.

    Parameters: - w (int): Width in pixels - h (int): Height in pixels

    Notes: - Typically set via DisplayConfig during construction - Use this to change display size at runtime if needed

    "},{"location":"api_reference/graphics/renderer/#int-getwidth-const","title":"int getWidth() const","text":"

    Gets the display width.

    Returns: - int: Display width in pixels

    "},{"location":"api_reference/graphics/renderer/#int-getheight-const","title":"int getHeight() const","text":"

    Gets the display height.

    Returns: - int: Display height in pixels

    "},{"location":"api_reference/graphics/renderer/#int-getxoffset-const","title":"int getXOffset() const","text":"

    Gets the current X display offset.

    Returns: - int: X offset in pixels

    "},{"location":"api_reference/graphics/renderer/#int-getyoffset-const","title":"int getYOffset() const","text":"

    Gets the current Y display offset.

    Returns: - int: Y offset in pixels

    "},{"location":"api_reference/graphics/renderer/#void-setcontrastuint8_t-level","title":"void setContrast(uint8_t level)","text":"

    Sets the display contrast (brightness).

    Parameters: - level (uint8_t): Contrast level (0-255)

    Notes: - Platform-specific: may not be supported on all displays - Higher values = brighter display

    "},{"location":"api_reference/graphics/renderer/#void-setfontconst-uint8_t-font","title":"void setFont(const uint8_t* font)","text":"

    Sets the font for text rendering.

    Parameters: - font (const uint8_t*): Pointer to the font data

    Notes: - Sets the default font for drawText() calls without font parameter - Use font constants like FONT_5X7 from Font.h

    "},{"location":"api_reference/graphics/renderer/#usage-example","title":"Usage Example","text":"
    #include \"graphics/Renderer.h\"\n#include \"graphics/DisplayConfig.h\"\n\nvoid draw(Renderer& renderer) override {\n    renderer.beginFrame();\n\n    // Draw background\n    renderer.drawFilledRectangle(0, 0, 128, 128, Color::Black);\n\n    // Draw sprites\n    renderer.drawSprite(playerSprite, playerX, playerY, Color::White);\n    renderer.drawSprite(enemySprite, enemyX, enemyY, Color::Red);\n\n    // Draw UI\n    renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n    renderer.drawTextCentered(\"Game Over\", 64, Color::Yellow, 2);\n\n    renderer.endFrame();\n}\n
    "},{"location":"api_reference/graphics/renderer/#performance-considerations","title":"Performance Considerations","text":"
    • Integer-only math: All operations use integer arithmetic for ESP32 efficiency
    • Sprite storage: Store sprite data in flash (const/constexpr) for best performance
    • Batch operations: Group similar draw calls together
    • Tilemaps: Use tilemaps for backgrounds instead of individual sprites
    • Viewport culling: Only draw what's visible on screen
    "},{"location":"api_reference/graphics/renderer/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Sprite data should be in flash, not RAM
    • Frame rate: Limit draw calls per frame for consistent FPS
    • Display offset: Use for scrolling instead of redrawing everything
    "},{"location":"api_reference/graphics/renderer/#see-also","title":"See Also","text":"
    • Sprite - Sprite structure
    • MultiSprite - Multi-layer sprites
    • TileMap - Tilemap structure
    • Color - Color constants
    • DisplayConfig - Display configuration
    • Camera2D - Camera for scrolling
    • Manual - Basic Rendering
    • Manual - Sprites and Animation
    • API Overview
    "},{"location":"api_reference/graphics/sprite/","title":"Sprite","text":"

    Low-level bitmap descriptor and multi-layer composition for retro rendering.

    "},{"location":"api_reference/graphics/sprite/#description","title":"Description","text":"

    Sprites are the fundamental graphics primitive in PixelRoot32. The engine supports multiple sprite formats:

    • 1bpp (Standard): Monochrome sprites, most memory-efficient
    • 2bpp (Experimental): 4 colors per sprite
    • 4bpp (Experimental): 16 colors per sprite
    • MultiSprite: Multi-layer 1bpp sprites for multi-color effects
    "},{"location":"api_reference/graphics/sprite/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    struct Sprite {\n        // ...\n    };\n\n    struct MultiSprite {\n        // ...\n    };\n\n    struct SpriteLayer {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/sprite/#sprite-structure-1bpp","title":"Sprite Structure (1bpp)","text":"

    Compact sprite descriptor for monochrome bitmapped sprites.

    "},{"location":"api_reference/graphics/sprite/#members","title":"Members","text":"
    • const uint16_t* data: Pointer to packed row data (size = height)
    • uint8_t width: Sprite width in pixels (\u2264 16)
    • uint8_t height: Sprite height in pixels
    "},{"location":"api_reference/graphics/sprite/#data-format","title":"Data Format","text":"

    Sprites are stored as an array of 16-bit rows. Each row packs horizontal pixels into bits:

    • Bit 0: Leftmost pixel of the row
    • Bit (width - 1): Rightmost pixel of the row
    • Bit value 1: Pixel on (colored)
    • Bit value 0: Pixel off (transparent/background)

    Only the lowest width bits of each row are used.

    "},{"location":"api_reference/graphics/sprite/#example","title":"Example","text":"
    // 8x8 sprite (smiley face)\nstatic const uint16_t SMILEY_DATA[] = {\n    0b00111100,  // Row 0:  \u2588\u2588\u2588\u2588\n    0b01111110,  // Row 1:  \u2588\u2588\u2588\u2588\u2588\u2588\n    0b11011011,  // Row 2:  \u2588\u2588 \u2588\u2588 \u2588\u2588\n    0b11111111,  // Row 3:  \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\n    0b11011011,  // Row 4:  \u2588\u2588 \u2588\u2588 \u2588\u2588\n    0b01100110,  // Row 5:  \u2588\u2588  \u2588\u2588\n    0b01111110,  // Row 6:  \u2588\u2588\u2588\u2588\u2588\u2588\n    0b00111100   // Row 7:  \u2588\u2588\u2588\u2588\n};\n\nstatic const Sprite SMILEY_SPRITE = {\n    SMILEY_DATA,\n    8,  // width\n    8   // height\n};\n
    "},{"location":"api_reference/graphics/sprite/#spritelayer-structure","title":"SpriteLayer Structure","text":"

    Single monochrome layer used by layered sprites.

    "},{"location":"api_reference/graphics/sprite/#members_1","title":"Members","text":"
    • const uint16_t* data: Pointer to packed row data for this layer
    • Color color: Color used for \"on\" pixels in this layer
    "},{"location":"api_reference/graphics/sprite/#notes","title":"Notes","text":"
    • Each layer uses the same width/height as its owning MultiSprite
    • Layers can have different colors
    • Layers are drawn in array order
    "},{"location":"api_reference/graphics/sprite/#multisprite-structure","title":"MultiSprite Structure","text":"

    Multi-layer, multi-color sprite built from 1bpp layers.

    "},{"location":"api_reference/graphics/sprite/#members_2","title":"Members","text":"
    • uint8_t width: Sprite width in pixels (\u2264 16)
    • uint8_t height: Sprite height in pixels
    • const SpriteLayer* layers: Pointer to array of layers
    • uint8_t layerCount: Number of layers in the array
    "},{"location":"api_reference/graphics/sprite/#notes_1","title":"Notes","text":"
    • Combines several 1bpp layers with different colors
    • Layers are drawn in array order
    • Enables multi-color sprites without higher bit-depths
    • More layers = more draw calls (but still efficient)
    "},{"location":"api_reference/graphics/sprite/#example_1","title":"Example","text":"
    // Outline layer\nstatic const uint16_t OUTLINE_DATA[] = {\n    0b11111111,\n    0b10000001,\n    0b10000001,\n    0b11111111\n};\n\n// Fill layer\nstatic const uint16_t FILL_DATA[] = {\n    0b00000000,\n    0b01111110,\n    0b01111110,\n    0b00000000\n};\n\nstatic const SpriteLayer LAYERS[] = {\n    {OUTLINE_DATA, Color::Black},  // Layer 0: Black outline\n    {FILL_DATA, Color::Red}       // Layer 1: Red fill\n};\n\nstatic const MultiSprite PLAYER_MULTISPRITE = {\n    8,      // width\n    8,      // height\n    LAYERS, // layers array\n    2       // layer count\n};\n
    "},{"location":"api_reference/graphics/sprite/#sprite2bpp-structure-experimental","title":"Sprite2bpp Structure (Experimental)","text":"

    2-bit per pixel sprite (4 colors).

    Requires: PIXELROOT32_ENABLE_2BPP_SPRITES build flag

    "},{"location":"api_reference/graphics/sprite/#members_3","title":"Members","text":"
    • const uint8_t* data: Packed 2bpp data
    • const Color* palette: Local palette (4 colors)
    • uint8_t width: Sprite width
    • uint8_t height: Sprite height
    • uint8_t paletteSize: Number of colors (typically 4)
    "},{"location":"api_reference/graphics/sprite/#notes_2","title":"Notes","text":"
    • Experimental feature
    • Uses more memory than 1bpp
    • Each pixel can be one of 4 colors from local palette
    "},{"location":"api_reference/graphics/sprite/#sprite4bpp-structure-experimental","title":"Sprite4bpp Structure (Experimental)","text":"

    4-bit per pixel sprite (16 colors).

    Requires: PIXELROOT32_ENABLE_4BPP_SPRITES build flag

    "},{"location":"api_reference/graphics/sprite/#members_4","title":"Members","text":"
    • const uint8_t* data: Packed 4bpp data
    • const Color* palette: Local palette (16 colors)
    • uint8_t width: Sprite width
    • uint8_t height: Sprite height
    • uint8_t paletteSize: Number of colors (typically 16)
    "},{"location":"api_reference/graphics/sprite/#notes_3","title":"Notes","text":"
    • Experimental feature
    • Uses more memory than 1bpp/2bpp
    • Each pixel can be one of 16 colors from local palette
    "},{"location":"api_reference/graphics/sprite/#sprite-animation","title":"Sprite Animation","text":""},{"location":"api_reference/graphics/sprite/#spriteanimationframe-structure","title":"SpriteAnimationFrame Structure","text":"

    Frame that can reference either a Sprite or a MultiSprite.

    Members: - const Sprite* sprite: Optional pointer to a simple 1bpp sprite frame - const MultiSprite* multiSprite: Optional pointer to a layered sprite frame

    Notes: - Exactly one pointer should be non-null for a valid frame - Allows same animation system for both sprite types

    "},{"location":"api_reference/graphics/sprite/#spriteanimation-structure","title":"SpriteAnimation Structure","text":"

    Lightweight, step-based sprite animation controller.

    Members: - const SpriteAnimationFrame* frames: Pointer to immutable frame table - uint8_t frameCount: Number of frames in the table - uint8_t current: Current frame index [0, frameCount)

    Methods: - void reset(): Reset to first frame - void step(): Advance to next frame (wrapping) - const SpriteAnimationFrame& getCurrentFrame() const: Get current frame - const Sprite* getCurrentSprite() const: Get current simple sprite - const MultiSprite* getCurrentMultiSprite() const: Get current multi-sprite

    Example:

    static const SpriteAnimationFrame WALK_FRAMES[] = {\n    {&walkFrame1, nullptr},\n    {&walkFrame2, nullptr},\n    {&walkFrame3, nullptr},\n    {&walkFrame2, nullptr}  // Loop back\n};\n\nstatic SpriteAnimation walkAnimation = {\n    WALK_FRAMES,\n    4,    // frameCount\n    0     // current\n};\n\n// In update\nwalkAnimation.step();\nconst Sprite* currentSprite = walkAnimation.getCurrentSprite();\n

    "},{"location":"api_reference/graphics/sprite/#usage-examples","title":"Usage Examples","text":""},{"location":"api_reference/graphics/sprite/#creating-1bpp-sprites","title":"Creating 1bpp Sprites","text":"
    // 8x8 player sprite\nstatic const uint16_t PLAYER_DATA[] = {\n    0b00111100,\n    0b01111110,\n    0b11111111,\n    0b11011011,\n    0b11111111,\n    0b01111110,\n    0b00111100,\n    0b00011000\n};\n\nstatic const Sprite PLAYER_SPRITE = {\n    PLAYER_DATA,\n    8,  // width\n    8   // height\n};\n\n// Use in rendering\nrenderer.drawSprite(PLAYER_SPRITE, 100, 100, Color::White);\n
    "},{"location":"api_reference/graphics/sprite/#creating-multisprite","title":"Creating MultiSprite","text":"
    // Outline layer\nstatic const uint16_t OUTLINE[] = {\n    0b11111111,\n    0b10000001,\n    0b10000001,\n    0b11111111\n};\n\n// Fill layer\nstatic const uint16_t FILL[] = {\n    0b00000000,\n    0b01111110,\n    0b01111110,\n    0b00000000\n};\n\nstatic const SpriteLayer LAYERS[] = {\n    {OUTLINE, Color::Black},\n    {FILL, Color::Red}\n};\n\nstatic const MultiSprite ENEMY_SPRITE = {\n    8,      // width\n    8,      // height\n    LAYERS, // layers\n    2       // layer count\n};\n\n// Use in rendering\nrenderer.drawMultiSprite(ENEMY_SPRITE, 100, 100);\n
    "},{"location":"api_reference/graphics/sprite/#sprite-animation_1","title":"Sprite Animation","text":"
    class AnimatedActor : public pixelroot32::core::Actor {\nprivate:\n    SpriteAnimation animation;\n    unsigned long animTimer = 0;\n    unsigned long animInterval = 200;  // 200ms per frame\n\npublic:\n    void update(unsigned long deltaTime) override {\n        Actor::update(deltaTime);\n\n        animTimer += deltaTime;\n        if (animTimer >= animInterval) {\n            animTimer -= animInterval;\n            animation.step();\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        const Sprite* frame = animation.getCurrentSprite();\n        if (frame) {\n            renderer.drawSprite(*frame, \n                               static_cast<int>(x), \n                               static_cast<int>(y), \n                               Color::White);\n        }\n    }\n};\n
    "},{"location":"api_reference/graphics/sprite/#sprite-flipping","title":"Sprite Flipping","text":"

    Sprites can be flipped horizontally:

    // Draw normal\nrenderer.drawSprite(sprite, 100, 100, Color::White, false);\n\n// Draw flipped\nrenderer.drawSprite(sprite, 100, 100, Color::White, true);\n
    "},{"location":"api_reference/graphics/sprite/#performance-considerations","title":"Performance Considerations","text":"
    • 1bpp sprites: Most efficient (integer-only operations)
    • MultiSprite: Each layer is a separate draw call (still efficient)
    • 2bpp/4bpp: Experimental, uses more memory and CPU
    • Storage: Store sprite data in flash (const/constexpr) for best performance
    • Size limit: Sprites are limited to 16 pixels wide for 1bpp format
    "},{"location":"api_reference/graphics/sprite/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Store sprite data in flash, not RAM
    • Sprite size: Smaller sprites = faster drawing
    • Format choice: Use 1bpp when possible for best performance
    • MultiSprite: More layers = more draw calls (but acceptable)
    "},{"location":"api_reference/graphics/sprite/#see-also","title":"See Also","text":"
    • Renderer - Rendering system that draws sprites
    • Color - Color constants for sprites
    • Manual - Sprites and Animation
    • API Overview
    "},{"location":"api_reference/graphics/tilemap/","title":"TileMap","text":"

    Generic structure for tile-based background rendering.

    "},{"location":"api_reference/graphics/tilemap/#description","title":"Description","text":"

    TileMapGeneric<T> is a template structure for rendering tile-based backgrounds efficiently. It supports multiple bit-depths (1bpp, 2bpp, 4bpp) by using the appropriate sprite type for tiles.

    Tilemaps are ideal for large backgrounds, levels, and static environments. They support viewport culling (only visible tiles are drawn) for optimal performance.

    "},{"location":"api_reference/graphics/tilemap/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    template<typename T>\n    struct TileMapGeneric {\n        // ...\n    };\n\n    using TileMap = TileMapGeneric<Sprite>;\n    using TileMap2bpp = TileMapGeneric<Sprite2bpp>;\n    using TileMap4bpp = TileMapGeneric<Sprite4bpp>;\n}\n
    "},{"location":"api_reference/graphics/tilemap/#template-parameters","title":"Template Parameters","text":""},{"location":"api_reference/graphics/tilemap/#t","title":"T","text":"

    The sprite type used for tiles.

    Supported types: - Sprite (1bpp) - Sprite2bpp (2bpp) - Sprite4bpp (4bpp)

    "},{"location":"api_reference/graphics/tilemap/#structure","title":"Structure","text":""},{"location":"api_reference/graphics/tilemap/#uint8_t-indices","title":"uint8_t* indices","text":"

    Array of tile indices mapping to tile sprites.

    Type: uint8_t*

    Access: Read-write

    Notes: - Array size = width * height - Each value is an index into the tiles array - 0 = first tile, 1 = second tile, etc. - Should be stored in flash (const) for best performance

    Example:

    // 16x16 tilemap (256 tiles)\nstatic const uint8_t LEVEL_INDICES[] = {\n    0, 0, 0, 0, 1, 1, 1, 1, // Row 0\n    0, 2, 2, 2, 2, 2, 2, 0, // Row 1\n    // ... more rows\n};\n

    "},{"location":"api_reference/graphics/tilemap/#uint8_t-width","title":"uint8_t width","text":"

    Width of the tilemap in tiles.

    Type: uint8_t

    Access: Read-write

    Example:

    width = 16;  // 16 tiles wide\n

    "},{"location":"api_reference/graphics/tilemap/#uint8_t-height","title":"uint8_t height","text":"

    Height of the tilemap in tiles.

    Type: uint8_t

    Access: Read-write

    Example:

    height = 16;  // 16 tiles tall\n

    "},{"location":"api_reference/graphics/tilemap/#const-t-tiles","title":"const T* tiles","text":"

    Array of tile sprites of type T.

    Type: const T*

    Access: Read-only

    Notes: - Array of sprite pointers, one per unique tile - Indices reference this array - All tiles should be the same size - Should be stored in flash (const) for best performance

    Example (1bpp):

    static const Sprite TILE_SPRITES[] = {\n    EMPTY_TILE,   // Index 0\n    WALL_TILE,    // Index 1\n    FLOOR_TILE,   // Index 2\n    // ... more tiles\n};\n

    Example (2bpp):

    static const Sprite2bpp TILE_SPRITES_2BPP[] = {\n    TILE_GRASS,   // Index 0\n    TILE_DIRT,    // Index 1\n    // ... more tiles\n};\n

    "},{"location":"api_reference/graphics/tilemap/#uint8_t-tilewidth","title":"uint8_t tileWidth","text":"

    Width of each tile in pixels.

    Type: uint8_t

    Access: Read-write

    Notes: - All tiles must have the same width - Common values: 8, 16 pixels - Should match sprite width

    Example:

    tileWidth = 8;  // 8x8 tiles\n

    "},{"location":"api_reference/graphics/tilemap/#uint8_t-tileheight","title":"uint8_t tileHeight","text":"

    Height of each tile in pixels.

    Type: uint8_t

    Access: Read-write

    Notes: - All tiles must have the same height - Common values: 8, 16 pixels - Should match sprite height

    Example:

    tileHeight = 8;  // 8x8 tiles\n

    "},{"location":"api_reference/graphics/tilemap/#uint16_t-tilecount","title":"uint16_t tileCount","text":"

    Number of unique tiles in the tiles array.

    Type: uint16_t

    Access: Read-write

    Notes: - Must match the size of the tiles array - Indices must be < tileCount

    Example:

    tileCount = 16;  // 16 unique tiles\n

    "},{"location":"api_reference/graphics/tilemap/#creating-tilemaps","title":"Creating Tilemaps","text":""},{"location":"api_reference/graphics/tilemap/#step-1-create-tile-sprites","title":"Step 1: Create Tile Sprites","text":"
    // Empty tile (index 0)\nstatic const uint16_t EMPTY_DATA[] = {\n    0b00000000,\n    0b00000000,\n    0b00000000,\n    0b00000000,\n    0b00000000,\n    0b00000000,\n    0b00000000,\n    0b00000000\n};\n\n// Wall tile (index 1)\nstatic const uint16_t WALL_DATA[] = {\n    0b11111111,\n    0b10000001,\n    0b10000001,\n    0b10000001,\n    0b10000001,\n    0b10000001,\n    0b10000001,\n    0b11111111\n};\n\n// Floor tile (index 2)\nstatic const uint16_t FLOOR_DATA[] = {\n    0b00000000,\n    0b01111110,\n    0b01111110,\n    0b01111110,\n    0b01111110,\n    0b01111110,\n    0b01111110,\n    0b00000000\n};\n\nstatic const Sprite TILE_SPRITES[] = {\n    {EMPTY_DATA, 8, 8},  // Index 0\n    {WALL_DATA, 8, 8},   // Index 1\n    {FLOOR_DATA, 8, 8}   // Index 2\n};\n
    "},{"location":"api_reference/graphics/tilemap/#step-2-create-index-array","title":"Step 2: Create Index Array","text":"
    // 16x16 level (256 tiles)\n// 0 = empty, 1 = wall, 2 = floor\nstatic const uint8_t LEVEL_INDICES[] = {\n    // Row 0: Top wall\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n    // Row 1: Walls on sides\n    1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,\n    // Row 2\n    1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,\n    // ... more rows\n    // Row 15: Bottom wall\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1\n};\n
    "},{"location":"api_reference/graphics/tilemap/#step-3-create-tilemap-structure","title":"Step 3: Create TileMap Structure","text":"
    static const TileMap LEVEL_MAP = {\n    const_cast<uint8_t*>(LEVEL_INDICES),  // indices (non-const for struct)\n    16,        // width in tiles\n    16,        // height in tiles\n    TILE_SPRITES, // tile sprites array\n    8,         // tile width\n    8,         // tile height\n    3          // tile count\n};\n
    "},{"location":"api_reference/graphics/tilemap/#rendering-tilemaps","title":"Rendering Tilemaps","text":"

    Use Renderer::drawTileMap():

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Draw tilemap at origin (0, 0)\n    renderer.drawTileMap(LEVEL_MAP, 0, 0, Color::White);\n\n    // With camera offset\n    camera.apply(renderer);\n    renderer.drawTileMap(LEVEL_MAP, 0, 0, Color::White);\n}\n
    "},{"location":"api_reference/graphics/tilemap/#viewport-culling","title":"Viewport Culling","text":"

    Tilemaps automatically cull tiles outside the viewport:

    • Only visible tiles are drawn
    • Very efficient for large levels
    • Works with camera scrolling

    Example:

    // Large level (256x256 tiles)\n// Only tiles visible on screen are drawn\ncamera.apply(renderer);\nrenderer.drawTileMap(LARGE_LEVEL_MAP, 0, 0, Color::White);\n

    "},{"location":"api_reference/graphics/tilemap/#collision-detection-with-tilemaps","title":"Collision Detection with Tilemaps","text":"

    Check tile at world position:

    bool isSolidTile(int worldX, int worldY, const TileMap& map) {\n    int tileX = worldX / map.tileWidth;\n    int tileY = worldY / map.tileHeight;\n\n    if (tileX < 0 || tileX >= map.width || \n        tileY < 0 || tileY >= map.height) {\n        return true;  // Outside map = solid\n    }\n\n    int index = tileY * map.width + tileX;\n    uint8_t tileIndex = map.indices[index];\n\n    // Check if tile is solid (e.g., wall = index 1)\n    return (tileIndex == 1);\n}\n
    "},{"location":"api_reference/graphics/tilemap/#usage-example","title":"Usage Example","text":"
    #include \"graphics/TileMap.h\"\n#include \"graphics/Renderer.h\"\n\nclass LevelScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::TileMap levelMap;\n    pixelroot32::graphics::Camera2D camera;\n\npublic:\n    void init() override {\n        // Level map is already defined (see above)\n        // Create camera\n        auto& renderer = engine.getRenderer();\n        camera = pixelroot32::graphics::Camera2D(\n            renderer.getWidth(),\n            renderer.getHeight()\n        );\n\n        // Set level boundaries\n        int levelWidth = levelMap.width * levelMap.tileWidth;\n        int levelHeight = levelMap.height * levelMap.tileHeight;\n        camera.setBounds(0.0f, levelWidth - renderer.getWidth());\n        camera.setVerticalBounds(0.0f, levelHeight - renderer.getHeight());\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Camera follows player\n        if (player) {\n            camera.followTarget(player->x, player->y);\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Apply camera\n        camera.apply(renderer);\n\n        // Draw tilemap (viewport culling automatic)\n        renderer.drawTileMap(levelMap, 0, 0, Color::White);\n\n        // Draw entities\n        Scene::draw(renderer);\n\n        // Reset for UI\n        renderer.setDisplayOffset(0, 0);\n    }\n\n    bool checkTileCollision(float x, float y) {\n        int tileX = static_cast<int>(x) / levelMap.tileWidth;\n        int tileY = static_cast<int>(y) / levelMap.tileHeight;\n\n        if (tileX < 0 || tileX >= levelMap.width || \n            tileY < 0 || tileY >= levelMap.height) {\n            return true;  // Outside = solid\n        }\n\n        int index = tileY * levelMap.width + tileX;\n        uint8_t tile = levelMap.indices[index];\n        return (tile == 1);  // Wall tile\n    }\n};\n
    "},{"location":"api_reference/graphics/tilemap/#performance-considerations","title":"Performance Considerations","text":"
    • Viewport culling: Only visible tiles are drawn (automatic)
    • Tile reuse: Reuse tile sprites across the map
    • Index storage: Compact uint8_t indices (1 byte per tile)
    • Memory: Store indices and tiles in flash (const) for best performance
    • Tile size: Smaller tiles = more tiles to draw, but more detail
    "},{"location":"api_reference/graphics/tilemap/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Store tilemap data in flash, not RAM
    • Map size: Large maps use more flash memory
    • Tile count: Limit unique tiles to save memory
    • Culling: Viewport culling is essential for large levels
    "},{"location":"api_reference/graphics/tilemap/#see-also","title":"See Also","text":"
    • Renderer - Rendering system that draws tilemaps
    • Sprite - Sprite structure used for tiles
    • Camera2D - Camera for scrolling tilemaps
    • Manual - Tilemaps
    • API Overview
    "},{"location":"api_reference/physics/collision_system/","title":"CollisionSystem","text":"

    Manages collision detection between entities.

    "},{"location":"api_reference/physics/collision_system/#description","title":"Description","text":"

    CollisionSystem iterates through registered entities, checks if they are Actors, and performs AABB (Axis-Aligned Bounding Box) collision checks based on their collision layers and masks.

    The system automatically filters collisions using layers and masks, avoiding unnecessary checks. When a collision is detected, it triggers the onCollision() callback on both actors.

    "},{"location":"api_reference/physics/collision_system/#namespace","title":"Namespace","text":"
    namespace pixelroot32::physics {\n    class CollisionSystem {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/physics/collision_system/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Scene (manages collision system instance)
    "},{"location":"api_reference/physics/collision_system/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/physics/collision_system/#void-addentityentity-e","title":"void addEntity(Entity* e)","text":"

    Adds an entity to the collision system.

    Parameters: - e (pixelroot32::core::Entity*): Pointer to the entity to add

    Returns: - void

    Notes: - Only Actor entities participate in collisions - Entities are automatically added when added to Scene - Typically not called directly (handled by Scene)

    Example:

    // Typically handled automatically by Scene\nscene->addEntity(actor);  // Automatically added to collision system\n

    "},{"location":"api_reference/physics/collision_system/#void-removeentityentity-e","title":"void removeEntity(Entity* e)","text":"

    Removes an entity from the collision system.

    Parameters: - e (pixelroot32::core::Entity*): Pointer to the entity to remove

    Returns: - void

    Notes: - Entities are automatically removed when removed from Scene - Typically not called directly

    Example:

    // Typically handled automatically by Scene\nscene->removeEntity(actor);  // Automatically removed from collision system\n

    "},{"location":"api_reference/physics/collision_system/#void-update","title":"void update()","text":"

    Performs collision detection for all registered entities.

    Returns: - void

    Notes: - Called automatically by Scene::update() - Iterates through all pairs of entities - Only checks collisions between Actors with matching layers/masks - Triggers onCollision() callbacks when collisions are detected - Uses AABB (Axis-Aligned Bounding Box) collision detection

    Example:

    // Called automatically by Scene, but can be called manually:\nvoid update(unsigned long deltaTime) override {\n    Scene::update(deltaTime);  // Calls collisionSystem.update()\n\n    // Or manually:\n    collisionSystem.update();\n}\n

    "},{"location":"api_reference/physics/collision_system/#how-it-works","title":"How It Works","text":"
    1. Entity Registration: Entities added to Scene are automatically registered
    2. Layer Filtering: Only actors with matching layers/masks are checked
    3. AABB Check: Uses Rect::intersects() for collision detection
    4. Callback: Calls onCollision() on both actors when collision detected
    "},{"location":"api_reference/physics/collision_system/#collision-detection-algorithm","title":"Collision Detection Algorithm","text":"
    for each actor1 in entities:\n    if actor1 is not an Actor: continue\n    for each actor2 in entities:\n        if actor2 is not an Actor: continue\n        if actor1 == actor2: continue\n\n        // Check layers/masks\n        if (actor1->layer & actor2->mask) == 0: continue\n        if (actor2->layer & actor1->mask) == 0: continue\n\n        // Check AABB intersection\n        Rect box1 = actor1->getHitBox();\n        Rect box2 = actor2->getHitBox();\n\n        if (box1.intersects(box2)) {\n            actor1->onCollision(actor2);\n            actor2->onCollision(actor1);\n        }\n
    "},{"location":"api_reference/physics/collision_system/#usage-example","title":"Usage Example","text":"
    #include \"physics/CollisionSystem.h\"\n#include \"core/Actor.h\"\n\nclass GameScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Create actors with collision layers\n        PlayerActor* player = new PlayerActor(64, 64);\n        player->layer = pixelroot32::physics::DefaultLayers::kPlayer;\n        player->mask = pixelroot32::physics::DefaultLayers::kEnemy | \n                       pixelroot32::physics::DefaultLayers::kObstacle;\n        addEntity(player);\n\n        EnemyActor* enemy = new EnemyActor(100, 100);\n        enemy->layer = pixelroot32::physics::DefaultLayers::kEnemy;\n        enemy->mask = pixelroot32::physics::DefaultLayers::kPlayer;\n        addEntity(enemy);\n\n        // Collision system is managed by Scene\n        // Collisions are checked automatically in Scene::update()\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Collision detection happens here automatically\n        Scene::update(deltaTime);\n    }\n};\n
    "},{"location":"api_reference/physics/collision_system/#performance-considerations","title":"Performance Considerations","text":"
    • Layer filtering: Very efficient; avoids most collision checks
    • AABB checks: Fast (simple rectangle intersection)
    • Pair checking: O(n\u00b2) complexity, but n is limited (MAX_ENTITIES = 32)
    • Update frequency: Called every frame; keep hitboxes simple
    "},{"location":"api_reference/physics/collision_system/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Entity limit: MAX_ENTITIES = 32 limits collision pairs
    • Layer efficiency: Use layers effectively to minimize checks
    • Hitbox simplicity: Keep hitboxes as simple AABB for best performance
    "},{"location":"api_reference/physics/collision_system/#see-also","title":"See Also","text":"
    • Actor - Actors that participate in collisions
    • CollisionTypes - Collision primitives and layers
    • Manual - Physics and Collisions
    • API Overview
    "},{"location":"api_reference/physics/collision_types/","title":"Collision Types","text":"

    Basic geometric types and collision layer definitions.

    "},{"location":"api_reference/physics/collision_types/#description","title":"Description","text":"

    This document describes the collision primitives (Rect, Circle, Segment) and collision layer system used by the collision detection system.

    "},{"location":"api_reference/physics/collision_types/#namespace","title":"Namespace","text":"
    namespace pixelroot32::physics {\n    // Types and functions\n}\n
    "},{"location":"api_reference/physics/collision_types/#collisionlayer-type","title":"CollisionLayer Type","text":"

    Collision layer type (bit flags).

    Type: uint16_t (typedef)

    Notes: - Uses bit flags for layer assignment - Actors can be on multiple layers (bitwise OR) - Masks define which layers an actor can collide with

    "},{"location":"api_reference/physics/collision_types/#defaultlayers-namespace","title":"DefaultLayers Namespace","text":"

    Predefined collision layers.

    "},{"location":"api_reference/physics/collision_types/#knone","title":"kNone","text":"

    No layer (0).

    Value: 0

    Usage:

    actor->layer = pixelroot32::physics::DefaultLayers::kNone;\n

    "},{"location":"api_reference/physics/collision_types/#kall","title":"kAll","text":"

    All layers (0xFFFF).

    Value: 0xFFFF

    Usage:

    actor->mask = pixelroot32::physics::DefaultLayers::kAll;  // Collide with everything\n

    "},{"location":"api_reference/physics/collision_types/#custom-layers","title":"Custom Layers","text":"

    Define custom layers using bit flags:

    namespace MyLayers {\n    const pixelroot32::physics::CollisionLayer kPlayer = 1 << 0;      // Bit 0\n    const pixelroot32::physics::CollisionLayer kEnemy = 1 << 1;       // Bit 1\n    const pixelroot32::physics::CollisionLayer kObstacle = 1 << 2;    // Bit 2\n    const pixelroot32::physics::CollisionLayer kProjectile = 1 << 3;  // Bit 3\n    const pixelroot32::physics::CollisionLayer kCollectible = 1 << 4;  // Bit 4\n}\n\n// Usage\nactor->layer = MyLayers::kPlayer;\nactor->mask = MyLayers::kEnemy | MyLayers::kObstacle;\n
    "},{"location":"api_reference/physics/collision_types/#circle-structure","title":"Circle Structure","text":"

    Represents a circle for collision detection.

    Members: - float x: Center X coordinate - float y: Center Y coordinate - float radius: Radius of the circle

    Example:

    pixelroot32::physics::Circle circle;\ncircle.x = 100.0f;\ncircle.y = 100.0f;\ncircle.radius = 10.0f;\n

    "},{"location":"api_reference/physics/collision_types/#segment-structure","title":"Segment Structure","text":"

    Represents a line segment for collision detection.

    Members: - float x1, y1: Start point coordinates - float x2, y2: End point coordinates

    Example:

    pixelroot32::physics::Segment segment;\nsegment.x1 = 0.0f;\nsegment.y1 = 0.0f;\nsegment.x2 = 100.0f;\nsegment.y2 = 100.0f;\n

    "},{"location":"api_reference/physics/collision_types/#intersection-functions","title":"Intersection Functions","text":""},{"location":"api_reference/physics/collision_types/#bool-intersectsconst-circle-a-const-circle-b","title":"bool intersects(const Circle& a, const Circle& b)","text":"

    Checks if two circles intersect.

    Parameters: - a (const Circle&): First circle - b (const Circle&): Second circle

    Returns: - bool: true if circles intersect

    Example:

    pixelroot32::physics::Circle circle1{100.0f, 100.0f, 10.0f};\npixelroot32::physics::Circle circle2{110.0f, 100.0f, 10.0f};\n\nif (pixelroot32::physics::intersects(circle1, circle2)) {\n    // Circles overlap\n}\n

    "},{"location":"api_reference/physics/collision_types/#bool-intersectsconst-circle-c-const-rect-r","title":"bool intersects(const Circle& c, const Rect& r)","text":"

    Checks if a circle and rectangle intersect.

    Parameters: - c (const Circle&): Circle - r (const Rect&): Rectangle

    Returns: - bool: true if circle and rectangle intersect

    Example:

    pixelroot32::physics::Circle circle{100.0f, 100.0f, 10.0f};\npixelroot32::core::Rect rect{95.0f, 95.0f, 20, 20};\n\nif (pixelroot32::physics::intersects(circle, rect)) {\n    // Circle overlaps rectangle\n}\n

    "},{"location":"api_reference/physics/collision_types/#bool-intersectsconst-segment-s-const-rect-r","title":"bool intersects(const Segment& s, const Rect& r)","text":"

    Checks if a line segment and rectangle intersect.

    Parameters: - s (const Segment&): Line segment - r (const Rect&): Rectangle

    Returns: - bool: true if segment and rectangle intersect

    Example:

    pixelroot32::physics::Segment segment{0.0f, 0.0f, 100.0f, 100.0f};\npixelroot32::core::Rect rect{50.0f, 50.0f, 20, 20};\n\nif (pixelroot32::physics::intersects(segment, rect)) {\n    // Segment intersects rectangle\n}\n

    "},{"location":"api_reference/physics/collision_types/#bool-sweepcirclevsrectconst-circle-start-const-circle-end-const-rect-target-float-thit","title":"bool sweepCircleVsRect(const Circle& start, const Circle& end, const Rect& target, float& tHit)","text":"

    Performs a sweep test: checks if a moving circle collides with a rectangle.

    Parameters: - start (const Circle&): Circle at start position - end (const Circle&): Circle at end position - target (const Rect&): Target rectangle - tHit (float&): Output parameter for collision time (0.0 to 1.0)

    Returns: - bool: true if collision occurs during sweep

    Notes: - Useful for fast-moving projectiles - tHit indicates when collision occurs (0.0 = start, 1.0 = end) - More expensive than simple AABB check

    Example:

    // Projectile moving from (0, 0) to (100, 100)\npixelroot32::physics::Circle start{0.0f, 0.0f, 5.0f};\npixelroot32::physics::Circle end{100.0f, 100.0f, 5.0f};\npixelroot32::core::Rect wall{50.0f, 50.0f, 20, 20};\n\nfloat tHit = 0.0f;\nif (pixelroot32::physics::sweepCircleVsRect(start, end, wall, tHit)) {\n    // Collision at time tHit\n    float hitX = 0.0f + (100.0f - 0.0f) * tHit;\n    float hitY = 0.0f + (100.0f - 0.0f) * tHit;\n}\n

    "},{"location":"api_reference/physics/collision_types/#rect-structure-from-coreentityh","title":"Rect Structure (from core/Entity.h)","text":"

    Represents a 2D rectangle for collision detection.

    Members: - float x, y: Top-left corner coordinates - int width, height: Dimensions

    Methods: - bool intersects(const Rect& other) const: Checks if rectangles overlap

    Example:

    pixelroot32::core::Rect rect1{10.0f, 20.0f, 50, 50};\npixelroot32::core::Rect rect2{30.0f, 40.0f, 50, 50};\n\nif (rect1.intersects(rect2)) {\n    // Rectangles overlap\n}\n

    "},{"location":"api_reference/physics/collision_types/#usage-examples","title":"Usage Examples","text":""},{"location":"api_reference/physics/collision_types/#layer-based-collision","title":"Layer-Based Collision","text":"
    // Define layers\nnamespace GameLayers {\n    const pixelroot32::physics::CollisionLayer kPlayer = 1 << 0;\n    const pixelroot32::physics::CollisionLayer kEnemy = 1 << 1;\n    const pixelroot32::physics::CollisionLayer kObstacle = 1 << 2;\n    const pixelroot32::physics::CollisionLayer kProjectile = 1 << 3;\n}\n\n// Player collides with enemies and obstacles\nplayer->layer = GameLayers::kPlayer;\nplayer->mask = GameLayers::kEnemy | GameLayers::kObstacle;\n\n// Enemy collides with player and obstacles\nenemy->layer = GameLayers::kEnemy;\nenemy->mask = GameLayers::kPlayer | GameLayers::kObstacle;\n\n// Projectile collides with enemies only\nprojectile->layer = GameLayers::kProjectile;\nprojectile->mask = GameLayers::kEnemy;\n
    "},{"location":"api_reference/physics/collision_types/#circle-vs-circle-collision","title":"Circle vs Circle Collision","text":"
    bool checkCollision(const Circle& a, const Circle& b) {\n    return pixelroot32::physics::intersects(a, b);\n}\n
    "},{"location":"api_reference/physics/collision_types/#sweep-test-for-fast-projectiles","title":"Sweep Test for Fast Projectiles","text":"
    class ProjectileActor : public pixelroot32::core::Actor {\nprivate:\n    float startX, startY;\n    float endX, endY;\n    float radius = 2.0f;\n\npublic:\n    bool checkWallCollision(const Rect& wall) {\n        pixelroot32::physics::Circle start{startX, startY, radius};\n        pixelroot32::physics::Circle end{endX, endY, radius};\n\n        float tHit = 0.0f;\n        if (pixelroot32::physics::sweepCircleVsRect(start, end, wall, tHit)) {\n            // Collision detected at time tHit\n            return true;\n        }\n        return false;\n    }\n};\n
    "},{"location":"api_reference/physics/collision_types/#performance-considerations","title":"Performance Considerations","text":"
    • AABB checks: Very fast (simple rectangle intersection)
    • Circle checks: Slightly slower (distance calculation)
    • Sweep tests: More expensive (use only for fast-moving objects)
    • Layer filtering: Essential for performance with many actors
    "},{"location":"api_reference/physics/collision_types/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Float math: Uses floating point; acceptable but integer math would be faster
    • Sweep tests: Use sparingly (more CPU intensive)
    • Layer efficiency: Use layers effectively to minimize checks
    "},{"location":"api_reference/physics/collision_types/#see-also","title":"See Also","text":"
    • CollisionSystem - Collision detection system
    • Actor - Actors that use collision layers
    • Manual - Physics and Collisions
    • API Overview
    "},{"location":"api_reference/ui/ui_button/","title":"UIButton","text":"

    A clickable button UI element.

    "},{"location":"api_reference/ui/ui_button/#description","title":"Description","text":"

    UIButton is a clickable button that supports both physical (keyboard/gamepad) and touch input. It can trigger a callback function when pressed and integrates with UI layouts for automatic navigation.

    Buttons support selection state (for D-pad navigation), custom styling, and text alignment.

    "},{"location":"api_reference/ui/ui_button/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIButton : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_button/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    • Inherited by: Your custom button classes (if needed)
    "},{"location":"api_reference/ui/ui_button/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_button/#uibuttonstring-t-uint8_t-index-float-x-float-y-float-w-float-h-function-callback-textalignment-textalign-center-int-fontsize-2","title":"UIButton(string t, uint8_t index, float x, float y, float w, float h, function callback, TextAlignment textAlign = CENTER, int fontSize = 2)

    Constructs a new UIButton.

    Parameters: - t (std::string): Button label text - index (uint8_t): Navigation index (for D-pad navigation in layouts) - x (float): X position - y (float): Y position - w (float): Width - h (float): Height - callback (std::function): Function to call when clicked/pressed - textAlign (TextAlignment, optional): Text alignment. Default: CENTER - fontSize (int, optional): Text size multiplier. Default: 2

    Example:

    #include \"graphics/ui/UIButton.h\"\n\nvoid onStartButtonClicked() {\n    // Start game\n    engine.setScene(&gameScene);\n}\n\nvoid onQuitButtonClicked() {\n    // Quit game\n    engine.stop();\n}\n\n// Create buttons\npixelroot32::graphics::ui::UIButton* startButton = new pixelroot32::graphics::ui::UIButton(\n    \"Start\",\n    0,  // index\n    64.0f, 64.0f,  // position\n    100.0f, 30.0f, // size\n    onStartButtonClicked,\n    pixelroot32::graphics::ui::TextAlignment::CENTER,\n    2\n);\n\npixelroot32::graphics::ui::UIButton* quitButton = new pixelroot32::graphics::ui::UIButton(\n    \"Quit\",\n    1,  // index\n    64.0f, 100.0f,\n    100.0f, 30.0f,\n    onQuitButtonClicked\n);\n

    ","text":""},{"location":"api_reference/ui/ui_button/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_button/#void-setstylecolor-textcol-color-bgcol-bool-drawbg","title":"void setStyle(Color textCol, Color bgCol, bool drawBg)

    Configures the button's visual style.

    Parameters: - textCol (Color): Color of the text - bgCol (Color): Color of the background - drawBg (bool): Whether to draw the background rectangle

    Returns: - void

    Example:

    button->setStyle(\n    pixelroot32::graphics::Color::White,  // Text color\n    pixelroot32::graphics::Color::Blue,   // Background color\n    true                                   // Draw background\n);\n

    ","text":""},{"location":"api_reference/ui/ui_button/#void-setselectedbool-selected","title":"void setSelected(bool selected)

    Sets the selection state (e.g., focused via D-pad).

    Parameters: - selected (bool): true if selected

    Returns: - void

    Notes: - Used by layouts for D-pad navigation - Selected buttons typically have different visual style - Can be set manually or automatically by layouts

    Example:

    button->setSelected(true);  // Highlight button\n

    ","text":""},{"location":"api_reference/ui/ui_button/#bool-getselected-const","title":"bool getSelected() const

    Checks if the button is currently selected.

    Returns: - bool: true if selected

    Example:

    if (button->getSelected()) {\n    // Button is focused\n}\n

    ","text":""},{"location":"api_reference/ui/ui_button/#bool-isfocusable-const-override","title":"bool isFocusable() const override

    Returns true (Buttons are always focusable).

    Returns: - bool: Always true

    ","text":""},{"location":"api_reference/ui/ui_button/#void-handleinputconst-inputmanager-input","title":"void handleInput(const InputManager& input)

    Handles input events. Checks for touch events within bounds or confirmation buttons if selected.

    Parameters: - input (const pixelroot32::input::InputManager&): The input manager instance

    Returns: - void

    Notes: - Should be called every frame in update() - Checks if button is selected and action button is pressed - Triggers callback if conditions are met

    Example:

    void update(unsigned long deltaTime) override {\n    UIElement::update(deltaTime);\n\n    auto& input = engine.getInputManager();\n    button->handleInput(input);\n}\n

    ","text":""},{"location":"api_reference/ui/ui_button/#void-press","title":"void press()

    Manually triggers the button's action.

    Returns: - void

    Notes: - Calls the button's callback function - Useful for programmatic button presses

    Example:

    button->press();  // Trigger button action\n

    ","text":""},{"location":"api_reference/ui/ui_button/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override

    Updates the button state.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    Notes: - Called automatically by Scene if isEnabled is true - Handles input and updates button state - Override to add custom update logic

    Example:

    void update(unsigned long deltaTime) override {\n    UIButton::update(deltaTime);\n\n    // Custom update logic\n    if (shouldPulse) {\n        // Animate button\n    }\n}\n

    ","text":""},{"location":"api_reference/ui/ui_button/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override

    Renders the button.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): The renderer to use

    Returns: - void

    Notes: - Called automatically by Scene if isVisible is true - Draws background (if enabled) and text - Selected state may change appearance

    Example:

    // Drawing is handled automatically\n// Override only for custom rendering\n

    ","text":""},{"location":"api_reference/ui/ui_button/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIButton.h\"\n#include \"core/Scene.h\"\n\nclass MainMenuScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIButton* startButton;\n    pixelroot32::graphics::ui::UIButton* quitButton;\n\npublic:\n    void init() override {\n        // Start button\n        startButton = new pixelroot32::graphics::ui::UIButton(\n            \"Start Game\",\n            0,  // index\n            64.0f, 50.0f,  // position (centered)\n            120.0f, 30.0f, // size\n            [this]() {\n                // Lambda callback\n                engine.setScene(&gameScene);\n            },\n            pixelroot32::graphics::ui::TextAlignment::CENTER,\n            2\n        );\n        startButton->setStyle(\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Blue,\n            true\n        );\n        addEntity(startButton);\n\n        // Quit button\n        quitButton = new pixelroot32::graphics::ui::UIButton(\n            \"Quit\",\n            1,  // index\n            64.0f, 90.0f,\n            120.0f, 30.0f,\n            [this]() {\n                // Quit game\n                engine.stop();\n            }\n        );\n        quitButton->setStyle(\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Red,\n            true\n        );\n        addEntity(quitButton);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Buttons handle input automatically\n        // Layouts handle navigation automatically\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw background\n        renderer.drawFilledRectangle(0, 0, 128, 128, Color::Black);\n\n        // Draw UI elements (buttons)\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_button/#button-navigation","title":"Button Navigation","text":"

    Buttons can be navigated with D-pad when in layouts:

    // Buttons in a vertical layout\npixelroot32::graphics::ui::UIVerticalLayout* layout = new UIVerticalLayout(64.0f, 50.0f);\nlayout->addChild(startButton);  // Index 0\nlayout->addChild(quitButton);   // Index 1\n\n// D-pad navigation is automatic\n// UP/DOWN moves selection\n// Action button (A) triggers selected button\n
    "},{"location":"api_reference/ui/ui_button/#performance-considerations","title":"Performance Considerations","text":"
    • Input handling: handleInput() is fast; safe to call every frame
    • Rendering: Simple rectangle and text; very efficient
    • Memory: Each button consumes memory (stay within MAX_ENTITIES)
    "},{"location":"api_reference/ui/ui_button/#esp32-considerations","title":"ESP32 Considerations","text":"
    • String storage: Button labels use std::string; consider memory usage
    • Callback functions: Use function pointers or lambdas (both efficient)
    "},{"location":"api_reference/ui/ui_button/#see-also","title":"See Also","text":"
    • UIElement - Base UI element class
    • UILabel - Text label
    • UILayouts - Layout containers
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_checkbox/","title":"UICheckBox","text":"

    A clickable checkbox UI element.

    "},{"location":"api_reference/ui/ui_checkbox/#description","title":"Description","text":"

    UICheckBox is a clickable checkbox that can be toggled between checked and unchecked states. It supports both physical (keyboard/gamepad) and touch input. It can trigger a callback function when its state changes and integrates with UI layouts for automatic navigation.

    "},{"location":"api_reference/ui/ui_checkbox/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UICheckBox : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_checkbox/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    "},{"location":"api_reference/ui/ui_checkbox/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_checkbox/#uicheckboxstring-label-uint8_t-index-float-x-float-y-float-w-float-h-bool-checked-false-function-callback-nullptr-int-fontsize-2","title":"UICheckBox(string label, uint8_t index, float x, float y, float w, float h, bool checked = false, function callback = nullptr, int fontSize = 2)

    Constructs a new UICheckBox.

    Parameters: - label (std::string): Checkbox label text - index (uint8_t): Navigation index (for D-pad navigation in layouts) - x (float): X position - y (float): Y position - w (float): Width - h (float): Height - checked (bool, optional): Initial checked state. Default: false - callback (std::function, optional): Function to call when the state changes. Default: nullptr - fontSize (int, optional): Text size multiplier. Default: 2

    Example:

    #include \"graphics/ui/UICheckBox.h\"\n\nvoid onCheckChanged(bool checked) {\n    if (checked) {\n        // Sound enabled\n    } else {\n        // Sound disabled\n    }\n}\n\n// Create checkbox\npixelroot32::graphics::ui::UICheckBox* soundCheckbox = new pixelroot32::graphics::ui::UICheckBox(\n    \"Enable Sound\",\n    0,  // index\n    64.0f, 64.0f,  // position\n    120.0f, 20.0f, // size\n    true,          // initial state\n    onCheckChanged,\n    1              // font size\n);\n

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_checkbox/#void-setstylecolor-textcol-color-bgcol-bool-drawbg-false","title":"void setStyle(Color textCol, Color bgCol, bool drawBg = false)

    Configures the checkbox's visual style.

    Parameters: - textCol (Color): Color of the text - bgCol (Color): Color of the background - drawBg (bool, optional): Whether to draw the background rectangle. Default: false

    Returns: - void

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#void-setcheckedbool-checked","title":"void setChecked(bool checked)

    Sets the checked state.

    Parameters: - checked (bool): True if checked

    Returns: - void

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#bool-ischecked-const","title":"bool isChecked() const

    Checks if the checkbox is currently checked.

    Returns: - bool: true if checked

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#void-toggle","title":"void toggle()

    Toggles the checkbox state and triggers the callback.

    Returns: - void

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#void-setselectedbool-selected","title":"void setSelected(bool selected)

    Sets the selection state (e.g., focused via D-pad).

    Parameters: - selected (bool): True if selected

    Returns: - void

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#bool-getselected-const","title":"bool getSelected() const

    Checks if the checkbox is currently selected.

    Returns: - bool: true if selected

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#callbacks","title":"Callbacks","text":""},{"location":"api_reference/ui/ui_checkbox/#oncheckchanged","title":"onCheckChanged

    The onCheckChanged callback is a std::function<void(bool)> that is triggered whenever the checkbox state changes via setChecked() or toggle().

    checkbox->onCheckChanged = [](bool isChecked) {\n    Serial.println(isChecked ? \"Checked!\" : \"Unchecked!\");\n};\n
    ","text":""},{"location":"api_reference/ui/ui_checkbox/#navigation-layouts","title":"Navigation & Layouts","text":"

    UICheckBox is designed to work seamlessly with UILayout containers (like UIVerticalLayout).

    • Focusable: Returns true for isFocusable(), allowing it to receive focus in a layout.
    • Input Handling: When selected (focused), it listens for the button index provided in the constructor (typically the 'A' button) to toggle its state.
    • Visual Feedback: When selected, it displays a selection indicator (usually a > character) if no background is drawn, or highlights its text/border.
    "},{"location":"api_reference/ui/ui_element/","title":"UIElement","text":"

    Base class for all user interface elements.

    "},{"location":"api_reference/ui/ui_element/#description","title":"Description","text":"

    UIElement is the base class for all UI components (buttons, labels, panels, etc.). It inherits from Entity to integrate with the scene graph and automatically sets the entity type to UI_ELEMENT and render layer to 2 (UI layer).

    "},{"location":"api_reference/ui/ui_element/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    enum class UIElementType {\n        GENERIC,\n        BUTTON,\n        LABEL,\n        CHECKBOX,\n        LAYOUT\n    };\n\n    class UIElement : public pixelroot32::core::Entity {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_element/#inheritance","title":"Inheritance","text":"
    • Inherits from: pixelroot32::core::Entity
    • Inherited by: UIButton, UICheckBox, UILabel, UIPanel, and all UI layouts
    "},{"location":"api_reference/ui/ui_element/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_element/#uielementfloat-x-float-y-float-w-float-h-uielementtype-type-uielementtypegeneric","title":"UIElement(float x, float y, float w, float h, UIElementType type = UIElementType::GENERIC)","text":"

    Constructs a new UIElement.

    Parameters: - x (float): X position - y (float): Y position - w (float): Width - h (float): Height - type (UIElementType): The type of the element (default: GENERIC)

    Notes: - Entity type is automatically set to UI_ELEMENT - Render layer is automatically set to 2 (UI layer) - Position and size can be modified after construction

    Example:

    class MyUIElement : public pixelroot32::graphics::ui::UIElement {\npublic:\n    MyUIElement(float x, float y) \n        : UIElement(x, y, 100.0f, 50.0f) {}\n\n    void update(unsigned long deltaTime) override {\n        // UI logic\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // UI rendering\n    }\n};\n

    "},{"location":"api_reference/ui/ui_element/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_element/#uielementtype-gettype-const","title":"UIElementType getType() const","text":"

    Returns the type of the UI element.

    Returns: - UIElementType: The type enum value (GENERIC, BUTTON, LABEL, CHECKBOX, or LAYOUT)

    "},{"location":"api_reference/ui/ui_element/#virtual-bool-isfocusable-const","title":"virtual bool isFocusable() const","text":"

    Checks if the element is focusable/selectable. Useful for navigation logic.

    Returns: - bool: true if focusable, false otherwise (default: false)

    "},{"location":"api_reference/ui/ui_element/#void-setpositionfloat-newx-float-newy","title":"void setPosition(float newX, float newY)","text":"

    Sets the position of the element.

    Parameters: - newX (float): New X coordinate - newY (float): New Y coordinate

    Returns: - void

    Notes: - Updates element position immediately - Use for manual positioning or animations

    Example:

    uiElement->setPosition(100.0f, 50.0f);\n

    "},{"location":"api_reference/ui/ui_element/#virtual-void-getpreferredsizefloat-preferredwidth-float-preferredheight-const","title":"virtual void getPreferredSize(float& preferredWidth, float& preferredHeight) const","text":"

    Gets the preferred size of the element. Used by layouts to determine how much space the element needs.

    Parameters: - preferredWidth (float&): Output parameter for preferred width (or -1 if flexible) - preferredHeight (float&): Output parameter for preferred height (or -1 if flexible)

    Returns: - void

    Notes: - Default implementation returns current width/height - Override in derived classes for custom sizing logic - Layouts use this to arrange elements

    Example:

    void getPreferredSize(float& w, float& h) const override {\n    // Custom sizing logic\n    w = static_cast<float>(width);\n    h = static_cast<float>(height);\n}\n

    "},{"location":"api_reference/ui/ui_element/#inherited-from-entity","title":"Inherited from Entity","text":"

    UIElement inherits all properties and methods from Entity:

    • float x, y: Position
    • int width, height: Dimensions
    • bool isVisible: Visibility state
    • bool isEnabled: Enabled state
    • unsigned char renderLayer: Render layer (automatically set to 2)
    • void setVisible(bool v): Set visibility
    • void setEnabled(bool e): Set enabled state
    • virtual void update(unsigned long deltaTime): Update logic
    • virtual void draw(Renderer& renderer): Drawing logic
    "},{"location":"api_reference/ui/ui_element/#textalignment-enum","title":"TextAlignment Enum","text":"

    Text alignment options for UI elements.

    Values: - TextAlignment::LEFT: Left-aligned text - TextAlignment::CENTER: Center-aligned text - TextAlignment::RIGHT: Right-aligned text

    "},{"location":"api_reference/ui/ui_element/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIElement.h\"\n\nclass CustomUIElement : public pixelroot32::graphics::ui::UIElement {\nprivate:\n    pixelroot32::graphics::Color bgColor;\n\npublic:\n    CustomUIElement(float x, float y, float w, float h) \n        : UIElement(x, y, w, h),\n          bgColor(pixelroot32::graphics::Color::Blue) {}\n\n    void update(unsigned long deltaTime) override {\n        // Update logic (if needed)\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        if (isVisible) {\n            // Draw background\n            renderer.drawFilledRectangle(\n                static_cast<int>(x),\n                static_cast<int>(y),\n                width,\n                height,\n                bgColor\n            );\n\n            // Draw border\n            renderer.drawRectangle(\n                static_cast<int>(x),\n                static_cast<int>(y),\n                width,\n                height,\n                pixelroot32::graphics::Color::White\n            );\n        }\n    }\n\n    void getPreferredSize(float& w, float& h) const override {\n        w = static_cast<float>(width);\n        h = static_cast<float>(height);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_element/#performance-considerations","title":"Performance Considerations","text":"
    • Render layer: UI elements are on layer 2, drawn after gameplay
    • Visibility: Use isVisible = false to hide elements efficiently
    • Layout integration: Layouts automatically manage element positioning
    "},{"location":"api_reference/ui/ui_element/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Each UI element consumes memory (stay within MAX_ENTITIES)
    • Object pooling: Reuse UI elements when possible
    • Update frequency: Disable UI elements that don't need to update
    "},{"location":"api_reference/ui/ui_element/#see-also","title":"See Also","text":"
    • Entity - Base entity class
    • UIButton - Clickable button
    • UILabel - Text label
    • UILayout - Layout containers
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_label/","title":"UILabel","text":"

    A simple text label UI element.

    "},{"location":"api_reference/ui/ui_label/#description","title":"Description","text":"

    UILabel displays a string of text on the screen. It auto-calculates its bounds based on text length and font size, making it easy to create dynamic text displays.

    Labels are useful for displaying scores, status messages, menu text, and other UI information.

    "},{"location":"api_reference/ui/ui_label/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UILabel : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_label/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    • Inherited by: Your custom label classes (if needed)
    "},{"location":"api_reference/ui/ui_label/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_label/#uilabelstring-t-float-x-float-y-color-col-uint8_t-sz","title":"UILabel(string t, float x, float y, Color col, uint8_t sz)","text":"

    Constructs a new UILabel.

    Parameters: - t (std::string): Initial text - x (float): X position - y (float): Y position - col (Color): Text color - sz (uint8_t): Text size multiplier

    Example:

    #include \"graphics/ui/UILabel.h\"\n\n// Create label\npixelroot32::graphics::ui::UILabel* scoreLabel = new pixelroot32::graphics::ui::UILabel(\n    \"Score: 0\",\n    10.0f, 10.0f,  // position\n    pixelroot32::graphics::Color::White,\n    1  // size\n);\n\n// Add to scene\nscene->addEntity(scoreLabel);\n

    "},{"location":"api_reference/ui/ui_label/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_label/#void-settextconst-string-t","title":"void setText(const string& t)","text":"

    Updates the label's text. Recalculates dimensions immediately using the active font metrics to ensure precise layout.

    Parameters: - t (const std::string&): New text

    Returns: - void

    Notes: - Automatically recalculates width and height using FontManager::textWidth - Use for dynamic text (scores, timers, etc.) - Text is stored as std::string (consider memory on ESP32)

    Example:

    // Update score label\nchar scoreText[32];\nsnprintf(scoreText, sizeof(scoreText), \"Score: %d\", score);\nscoreLabel->setText(scoreText);\n

    "},{"location":"api_reference/ui/ui_label/#void-setvisiblebool-v","title":"void setVisible(bool v)","text":"

    Sets visibility.

    Parameters: - v (bool): true to show, false to hide

    Returns: - void

    Notes: - Inherited from Entity - Hides label without removing from scene

    Example:

    label->setVisible(false);  // Hide\nlabel->setVisible(true);   // Show\n

    "},{"location":"api_reference/ui/ui_label/#void-centerxint-screenwidth","title":"void centerX(int screenWidth)","text":"

    Centers the label horizontally within a given width (typically the screen width). Recalculates dimensions before positioning to ensure perfect centering.

    Parameters: - screenWidth (int): The width to center within (e.g., engine.getRenderer().getWidth())

    Returns: - void

    Example:

    label->centerX(128); // Center on a 128px screen\n

    "},{"location":"api_reference/ui/ui_label/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the label state.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    Notes: - Called automatically by Scene if isEnabled is true - Default implementation does nothing - Override to add custom update logic (animations, etc.)

    Example:

    void update(unsigned long deltaTime) override {\n    UILabel::update(deltaTime);\n\n    // Custom logic (e.g., update text based on game state)\n    if (scoreChanged) {\n        updateScoreText();\n    }\n}\n

    "},{"location":"api_reference/ui/ui_label/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Renders the label.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): The renderer to use

    Returns: - void

    Notes: - Called automatically by Scene if isVisible is true - Draws text at label position - Uses default font

    Example:

    // Drawing is handled automatically\n// Override only for custom rendering\n

    "},{"location":"api_reference/ui/ui_label/#auto-sizing","title":"Auto-Sizing","text":"

    Labels automatically calculate their size based on text:

    • Width: text.length() * (6 * size) pixels
    • Height: 8 * size pixels

    Example:

    // Label with text \"Hello\" (5 characters), size 1\n// Width: 5 * 6 * 1 = 30 pixels\n// Height: 8 * 1 = 8 pixels\n\nUILabel label(\"Hello\", 10, 10, Color::White, 1);\n// label.width = 30, label.height = 8\n

    "},{"location":"api_reference/ui/ui_label/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UILabel.h\"\n\nclass GameScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UILabel* scoreLabel;\n    pixelroot32::graphics::ui::UILabel* livesLabel;\n    int score = 0;\n    int lives = 3;\n\npublic:\n    void init() override {\n        // Score label\n        scoreLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Score: 0\",\n            10.0f, 10.0f,\n            pixelroot32::graphics::Color::White,\n            1\n        );\n        addEntity(scoreLabel);\n\n        // Lives label\n        livesLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Lives: 3\",\n            10.0f, 25.0f,\n            pixelroot32::graphics::Color::Yellow,\n            1\n        );\n        addEntity(livesLabel);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Update labels\n        char text[32];\n        snprintf(text, sizeof(text), \"Score: %d\", score);\n        scoreLabel->setText(text);\n\n        snprintf(text, sizeof(text), \"Lives: %d\", lives);\n        livesLabel->setText(text);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw game\n        Scene::draw(renderer);\n\n        // Labels are drawn automatically by Scene::draw()\n    }\n};\n
    "},{"location":"api_reference/ui/ui_label/#centered-labels","title":"Centered Labels","text":"
    // Create centered title\npixelroot32::graphics::ui::UILabel* title = new pixelroot32::graphics::ui::UILabel(\n    \"Game Title\",\n    0, 20.0f,  // X will be adjusted\n    pixelroot32::graphics::Color::Yellow,\n    2  // Large text\n);\ntitle->centerX(128);  // Center on screen\naddEntity(title);\n
    "},{"location":"api_reference/ui/ui_label/#performance-considerations","title":"Performance Considerations","text":"
    • Text updates: setText() recalculates size; avoid calling every frame if text doesn't change
    • String storage: Uses std::string; consider memory on ESP32
    • Rendering: Simple text drawing; very efficient
    • Static text: For static text, create once and don't update
    "},{"location":"api_reference/ui/ui_label/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: std::string uses heap memory; use static buffers when possible
    • Text updates: Limit frequency of text updates
    • String length: Keep text short to save memory
    "},{"location":"api_reference/ui/ui_label/#see-also","title":"See Also","text":"
    • UIElement - Base UI element class
    • UIButton - Clickable button
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layout/","title":"UILayout","text":"

    Base class for UI layout containers.

    "},{"location":"api_reference/ui/ui_layout/#description","title":"Description","text":"

    UILayout is the base class for all layout containers. Layouts organize UI elements automatically, handling positioning, spacing, and optional scrolling. Layouts are themselves UI elements that can be added to scenes.

    "},{"location":"api_reference/ui/ui_layout/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UILayout : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layout/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    • Inherited by: UIVerticalLayout, UIHorizontalLayout, UIGridLayout, UIAnchorLayout
    "},{"location":"api_reference/ui/ui_layout/#scrollbehavior-enum","title":"ScrollBehavior Enum","text":"

    Defines how scrolling behaves in layouts.

    Values: - ScrollBehavior::NONE: No scrolling allowed - ScrollBehavior::SCROLL: Scroll freely within bounds - ScrollBehavior::CLAMP: Scroll but clamp to content bounds

    "},{"location":"api_reference/ui/ui_layout/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layout/#virtual-void-addelementuielement-element-0","title":"virtual void addElement(UIElement* element) = 0","text":"

    Adds a UI element to the layout. Must be implemented by derived classes.

    Parameters: - element (UIElement*): Pointer to the element to add

    "},{"location":"api_reference/ui/ui_layout/#virtual-void-removeelementuielement-element-0","title":"virtual void removeElement(UIElement* element) = 0","text":"

    Removes a UI element from the layout. Must be implemented by derived classes.

    Parameters: - element (UIElement*): Pointer to the element to remove

    "},{"location":"api_reference/ui/ui_layout/#virtual-void-updatelayout-0","title":"virtual void updateLayout() = 0","text":"

    Recalculates positions of all elements in the layout. Must be implemented by derived classes.

    Returns: - void

    Notes: - Should be called automatically when elements are added/removed

    "},{"location":"api_reference/ui/ui_layout/#virtual-void-handleinputconst-inputmanager-input-0","title":"virtual void handleInput(const InputManager& input) = 0","text":"

    Handles input for layout navigation (scroll, selection, etc.). Must be implemented by derived classes.

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    "},{"location":"api_reference/ui/ui_layout/#void-setpaddingfloat-p","title":"void setPadding(float p)","text":"

    Sets the padding (internal spacing) of the layout.

    Parameters: - p (float): Padding value in pixels

    Returns: - void

    Notes: - Layout is automatically recalculated

    "},{"location":"api_reference/ui/ui_layout/#float-getpadding-const","title":"float getPadding() const","text":"

    Gets the current padding.

    Returns: - float: Padding value in pixels

    "},{"location":"api_reference/ui/ui_layout/#void-setspacingfloat-s","title":"void setSpacing(float s)","text":"

    Sets the spacing between elements.

    Parameters: - s (float): Spacing value in pixels

    Returns: - void

    Notes: - Layout is automatically recalculated - Default: 4.0 pixels

    "},{"location":"api_reference/ui/ui_layout/#float-getspacing-const","title":"float getSpacing() const","text":"

    Gets the current spacing.

    Returns: - float: Spacing value in pixels

    "},{"location":"api_reference/ui/ui_layout/#size_t-getelementcount-const","title":"size_t getElementCount() const","text":"

    Gets the number of elements in the layout.

    Returns: - size_t: Element count

    "},{"location":"api_reference/ui/ui_layout/#uielement-getelementsize_t-index-const","title":"UIElement* getElement(size_t index) const","text":"

    Gets the element at a specific index.

    Parameters: - index (size_t): Element index

    Returns: - UIElement*: Pointer to the element, or nullptr if index is invalid

    "},{"location":"api_reference/ui/ui_layout/#void-clearelements","title":"void clearElements()","text":"

    Clears all elements from the layout.

    Returns: - void

    Notes: - Elements are not deleted (you must manage their lifetimes) - Layout is automatically recalculated

    "},{"location":"api_reference/ui/ui_layout/#protected-members","title":"Protected Members","text":"
    • std::vector<UIElement*> elements: List of child elements
    • float padding: Internal padding
    • float spacing: Spacing between elements (default: 4.0)
    • float scrollOffset: Current scroll offset
    • bool enableScroll: Whether scrolling is enabled
    • ScrollBehavior scrollBehavior: Scroll behavior mode
    "},{"location":"api_reference/ui/ui_layout/#see-also","title":"See Also","text":"
    • UIElement - Base UI element class
    • UIVerticalLayout - Vertical layout
    • UIHorizontalLayout - Horizontal layout
    • UIGridLayout - Grid layout
    • UIAnchorLayout - Anchor layout
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/","title":"UIAnchorLayout","text":"

    Layout that positions elements at fixed anchor points on the screen.

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#description","title":"Description","text":"

    UIAnchorLayout positions UI elements at fixed anchor points (corners, center, edges) without reflow. Very efficient for HUDs, debug UI, and fixed-position elements. Positions are calculated once or when screen size changes.

    This layout is ideal for HUD elements like score, lives, health bars, and other fixed-position UI.

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIAnchorLayout : public UILayout {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#inheritance","title":"Inheritance","text":"
    • Inherits from: UILayout
    • Inherited by: Your custom anchor layouts (if needed)
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#anchor-enum","title":"Anchor Enum","text":"

    Defines anchor points for positioning UI elements.

    Values: - Anchor::TOP_LEFT: Top-left corner - Anchor::TOP_RIGHT: Top-right corner - Anchor::BOTTOM_LEFT: Bottom-left corner - Anchor::BOTTOM_RIGHT: Bottom-right corner - Anchor::CENTER: Center of screen - Anchor::TOP_CENTER: Top center - Anchor::BOTTOM_CENTER: Bottom center - Anchor::LEFT_CENTER: Left center - Anchor::RIGHT_CENTER: Right center

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/anchor_layout/#uianchorlayoutfloat-x-float-y-float-w-float-h","title":"UIAnchorLayout(float x, float y, float w, float h)","text":"

    Constructs a new UIAnchorLayout.

    Parameters: - x (float): X position of the layout container (usually 0) - y (float): Y position of the layout container (usually 0) - w (float): Width of the layout container (usually screen width) - h (float): Height of the layout container (usually screen height)

    Example:

    #include \"graphics/ui/UIAnchorLayout.h\"\n\n// Create full-screen anchor layout for HUD\nauto& renderer = engine.getRenderer();\npixelroot32::graphics::ui::UIAnchorLayout* hud = \n    new pixelroot32::graphics::ui::UIAnchorLayout(\n        0.0f, 0.0f,\n        static_cast<float>(renderer.getWidth()),\n        static_cast<float>(renderer.getHeight())\n    );\nhud->setScreenSize(\n    static_cast<float>(renderer.getWidth()),\n    static_cast<float>(renderer.getHeight())\n);\n

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-addelementuielement-element-anchor-anchor","title":"void addElement(UIElement* element, Anchor anchor)","text":"

    Adds a UI element with a specific anchor point.

    Parameters: - element (UIElement*): Pointer to the element to add - anchor (Anchor): Anchor point for positioning

    Returns: - void

    Example:

    // Score label at top-right\nhud->addElement(scoreLabel, pixelroot32::graphics::ui::Anchor::TOP_RIGHT);\n\n// Lives label at top-left\nhud->addElement(livesLabel, pixelroot32::graphics::ui::Anchor::TOP_LEFT);\n

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-addelementuielement-element","title":"void addElement(UIElement* element)","text":"

    Adds a UI element to the layout (defaults to TOP_LEFT anchor).

    Parameters: - element (UIElement*): Pointer to the element to add

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-removeelementuielement-element","title":"void removeElement(UIElement* element)","text":"

    Removes a UI element from the layout.

    Parameters: - element (UIElement*): Pointer to the element to remove

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-updatelayout","title":"void updateLayout()","text":"

    Recalculates positions of all elements based on anchors.

    Returns: - void

    Notes: - Called automatically when screen size changes - Can be called manually if needed

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-handleinputconst-inputmanager-input","title":"void handleInput(const InputManager& input)","text":"

    Handles input (no-op for anchor layout, elements handle their own input).

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    Returns: - void

    Notes: - Anchor layout doesn't handle navigation - Elements handle their own input

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the layout and child elements.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws all elements.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-setscreensizefloat-screenwidth-float-screenheight","title":"void setScreenSize(float screenWidth, float screenHeight)","text":"

    Sets the screen size for anchor calculations.

    Parameters: - screenWidth (float): Screen width in pixels - screenHeight (float): Screen height in pixels

    Returns: - void

    Notes: - Used to calculate anchor positions - Should match actual display size - Layout is automatically recalculated

    Example:

    auto& renderer = engine.getRenderer();\nhud->setScreenSize(\n    static_cast<float>(renderer.getWidth()),\n    static_cast<float>(renderer.getHeight())\n);\n

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#float-getscreenwidth-const","title":"float getScreenWidth() const","text":"

    Gets the screen width.

    Returns: - float: Screen width in pixels

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#float-getscreenheight-const","title":"float getScreenHeight() const","text":"

    Gets the screen height.

    Returns: - float: Screen height in pixels

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIAnchorLayout.h\"\n\nclass GameScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIAnchorLayout* hud;\n    pixelroot32::graphics::ui::UILabel* scoreLabel;\n    pixelroot32::graphics::ui::UILabel* livesLabel;\n\npublic:\n    void init() override {\n        auto& renderer = engine.getRenderer();\n\n        // Create HUD layout\n        hud = new pixelroot32::graphics::ui::UIAnchorLayout(\n            0.0f, 0.0f,\n            static_cast<float>(renderer.getWidth()),\n            static_cast<float>(renderer.getHeight())\n        );\n        hud->setScreenSize(\n            static_cast<float>(renderer.getWidth()),\n            static_cast<float>(renderer.getHeight())\n        );\n\n        // Score label (top-right)\n        scoreLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Score: 0\",\n            0, 0,\n            Color::White,\n            1\n        );\n        hud->addElement(scoreLabel, \n                        pixelroot32::graphics::ui::Anchor::TOP_RIGHT);\n\n        // Lives label (top-left)\n        livesLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Lives: 3\",\n            0, 0,\n            Color::Yellow,\n            1\n        );\n        hud->addElement(livesLabel, \n                        pixelroot32::graphics::ui::Anchor::TOP_LEFT);\n\n        addEntity(hud);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Update labels\n        char text[32];\n        snprintf(text, sizeof(text), \"Score: %d\", score);\n        scoreLabel->setText(text);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw game\n        Scene::draw(renderer);\n\n        // HUD is drawn automatically (on layer 2)\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#anchor-positioning","title":"Anchor Positioning","text":"

    Elements are positioned based on their anchor:

    • TOP_LEFT: Element's top-left at screen top-left
    • TOP_RIGHT: Element's top-right at screen top-right
    • BOTTOM_LEFT: Element's bottom-left at screen bottom-left
    • BOTTOM_RIGHT: Element's bottom-right at screen bottom-right
    • CENTER: Element centered on screen
    • TOP_CENTER: Element centered horizontally, top-aligned
    • BOTTOM_CENTER: Element centered horizontally, bottom-aligned
    • LEFT_CENTER: Element centered vertically, left-aligned
    • RIGHT_CENTER: Element centered vertically, right-aligned
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#performance-considerations","title":"Performance Considerations","text":"
    • No reflow: Very efficient (positions calculated once)
    • Fixed positions: Ideal for HUD elements
    • Viewport independent: Elements stay in fixed screen positions
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Very efficient (no complex calculations)
    • Update frequency: Positions only recalculate when screen size changes
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#see-also","title":"See Also","text":"
    • UILayout - Base layout class
    • UILabel - Labels for HUD
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/grid_layout/","title":"UIGridLayout","text":"

    Grid layout container for organizing elements in a matrix.

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#description","title":"Description","text":"

    UIGridLayout organizes UI elements in a fixed grid of rows and columns. It supports navigation in 4 directions (UP/DOWN/LEFT/RIGHT) and automatic positioning based on grid coordinates.

    This layout is ideal for inventories, level selection screens, galleries, and any grid-based UI arrangement.

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIGridLayout : public UILayout {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#inheritance","title":"Inheritance","text":"
    • Inherits from: UILayout
    • Inherited by: Your custom grid layouts (if needed)
    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/grid_layout/#uigridlayoutfloat-x-float-y-float-w-float-h","title":"UIGridLayout(float x, float y, float w, float h)","text":"

    Constructs a new UIGridLayout.

    Parameters: - x (float): X position of the layout container - y (float): Y position of the layout container - w (float): Width of the layout container - h (float): Height of the layout container

    Example:

    #include \"graphics/ui/UIGridLayout.h\"\n\n// Create 4x4 grid for inventory\npixelroot32::graphics::ui::UIGridLayout* inventory = \n    new pixelroot32::graphics::ui::UIGridLayout(\n        10.0f, 10.0f,  // position\n        108.0f, 108.0f // size\n    );\ninventory->setColumns(4);\n

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-addelementuielement-element","title":"void addElement(UIElement* element)","text":"

    Adds a UI element to the layout.

    Parameters: - element (UIElement*): Pointer to the element to add

    Returns: - void

    Notes: - Elements are arranged in grid order (left to right, top to bottom) - Layout is automatically recalculated - Cell size is calculated based on columns and layout size

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-removeelementuielement-element","title":"void removeElement(UIElement* element)","text":"

    Removes a UI element from the layout.

    Parameters: - element (UIElement*): Pointer to the element to remove

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-updatelayout","title":"void updateLayout()","text":"

    Recalculates positions of all elements.

    Returns: - void

    Notes: - Recalculates cell dimensions - Recalculates row count - Repositions all elements

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-handleinputconst-inputmanager-input","title":"void handleInput(const InputManager& input)","text":"

    Handles input for navigation and selection.

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    Returns: - void

    Notes: - Handles UP/DOWN/LEFT/RIGHT navigation - Manages selection state - Wraps around grid edges (if configured)

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the layout and child elements.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws the layout and its visible elements.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-setcolumnsuint8_t-cols","title":"void setColumns(uint8_t cols)","text":"

    Sets the number of columns in the grid.

    Parameters: - cols (uint8_t): Number of columns (must be > 0)

    Returns: - void

    Notes: - Layout is automatically recalculated - Row count is calculated based on element count and columns

    Example:

    inventory->setColumns(4);  // 4 columns\n

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#uint8_t-getcolumns-const","title":"uint8_t getColumns() const","text":"

    Gets the number of columns.

    Returns: - uint8_t: Number of columns

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#uint8_t-getrows-const","title":"uint8_t getRows() const","text":"

    Gets the number of rows (calculated).

    Returns: - uint8_t: Number of rows

    Notes: - Calculated as ceil(elementCount / columns)

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#int-getselectedindex-const","title":"int getSelectedIndex() const","text":"

    Gets the currently selected element index.

    Returns: - int: Selected index, or -1 if none selected

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-setselectedindexint-index","title":"void setSelectedIndex(int index)","text":"

    Sets the selected element index.

    Parameters: - index (int): Index to select (-1 to deselect)

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#uielement-getselectedelement-const","title":"UIElement* getSelectedElement() const","text":"

    Gets the selected element.

    Returns: - UIElement*: Pointer to selected element, or nullptr if none selected

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-setnavigationbuttonsuint8_t-upbutton-uint8_t-downbutton-uint8_t-leftbutton-uint8_t-rightbutton","title":"void setNavigationButtons(uint8_t upButton, uint8_t downButton, uint8_t leftButton, uint8_t rightButton)","text":"

    Sets the navigation button indices.

    Parameters: - upButton (uint8_t): Button index for UP navigation - downButton (uint8_t): Button index for DOWN navigation - leftButton (uint8_t): Button index for LEFT navigation - rightButton (uint8_t): Button index for RIGHT navigation

    Returns: - void

    Notes: - Default: UP=0, DOWN=1, LEFT=2, RIGHT=3 - Change if your input mapping differs

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-setbuttonstylecolor-selectedtextcol-color-selectedbgcol-color-unselectedtextcol-color-unselectedbgcol","title":"void setButtonStyle(Color selectedTextCol, Color selectedBgCol, Color unselectedTextCol, Color unselectedBgCol)","text":"

    Sets the style colors for selected and unselected buttons.

    Parameters: - selectedTextCol (Color): Text color when selected - selectedBgCol (Color): Background color when selected - unselectedTextCol (Color): Text color when not selected - unselectedBgCol (Color): Background color when not selected

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIGridLayout.h\"\n\nclass InventoryScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIGridLayout* inventory;\n\npublic:\n    void init() override {\n        // Create 4x4 inventory grid\n        inventory = new pixelroot32::graphics::ui::UIGridLayout(\n            10.0f, 10.0f,\n            108.0f, 108.0f\n        );\n        inventory->setColumns(4);\n        inventory->setSpacing(2.0f);\n        inventory->setPadding(4.0f);\n\n        // Add inventory slots\n        for (int i = 0; i < 16; i++) {\n            auto* slot = createInventorySlot(i);\n            inventory->addElement(slot);\n        }\n\n        addEntity(inventory);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#see-also","title":"See Also","text":"
    • UILayout - Base layout class (abstract)
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/","title":"UIHorizontalLayout","text":"

    Horizontal layout container with scroll support.

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#description","title":"Description","text":"

    UIHorizontalLayout organizes UI elements horizontally, one next to another. It supports scrolling when content exceeds the visible viewport and handles keyboard/D-pad navigation automatically.

    This layout is ideal for toolbars, tab bars, horizontal menus, and any horizontal arrangement of UI elements.

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIHorizontalLayout : public UILayout {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#inheritance","title":"Inheritance","text":"
    • Inherits from: UILayout
    • Inherited by: Your custom horizontal layouts (if needed)
    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#uihorizontallayoutfloat-x-float-y-float-w-float-h","title":"UIHorizontalLayout(float x, float y, float w, float h)","text":"

    Constructs a new UIHorizontalLayout.

    Parameters: - x (float): X position of the layout container - y (float): Y position of the layout container - w (float): Width of the layout container (viewport width) - h (float): Height of the layout container

    Example:

    #include \"graphics/ui/UIHorizontalLayout.h\"\n\n// Create horizontal layout for toolbar\npixelroot32::graphics::ui::UIHorizontalLayout* toolbar = \n    new pixelroot32::graphics::ui::UIHorizontalLayout(\n        0.0f, 0.0f,   // position (top of screen)\n        128.0f, 20.0f // size\n    );\n

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-addelementuielement-element","title":"void addElement(UIElement* element)","text":"

    Adds a UI element to the layout.

    Parameters: - element (UIElement*): Pointer to the element to add

    Returns: - void

    Notes: - Elements are arranged horizontally, left to right - Layout is automatically recalculated - Elements are positioned based on spacing and padding

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-removeelementuielement-element","title":"void removeElement(UIElement* element)","text":"

    Removes a UI element from the layout.

    Parameters: - element (UIElement*): Pointer to the element to remove

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-updatelayout","title":"void updateLayout()","text":"

    Recalculates positions of all elements.

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-handleinputconst-inputmanager-input","title":"void handleInput(const InputManager& input)","text":"

    Handles input for navigation and scrolling.

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    Returns: - void

    Notes: - Handles LEFT/RIGHT navigation - Manages selection state - Handles scrolling if enabled

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the layout (handles smooth scrolling).

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws the layout and its visible elements.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setscrollenabledbool-enable","title":"void setScrollEnabled(bool enable)","text":"

    Enables or disables scrolling.

    Parameters: - enable (bool): true to enable scrolling

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setviewportwidthfloat-w","title":"void setViewportWidth(float w)","text":"

    Sets the viewport width (visible area).

    Parameters: - w (float): Viewport width in pixels

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#float-getscrolloffset-const","title":"float getScrollOffset() const","text":"

    Gets the current scroll offset.

    Returns: - float: Scroll offset in pixels

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setscrolloffsetfloat-offset","title":"void setScrollOffset(float offset)","text":"

    Sets the scroll offset directly.

    Parameters: - offset (float): Scroll offset in pixels

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#float-getcontentwidth-const","title":"float getContentWidth() const","text":"

    Gets the total content width.

    Returns: - float: Content width in pixels

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#int-getselectedindex-const","title":"int getSelectedIndex() const","text":"

    Gets the currently selected element index.

    Returns: - int: Selected index, or -1 if none selected

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setselectedindexint-index","title":"void setSelectedIndex(int index)","text":"

    Sets the selected element index.

    Parameters: - index (int): Index to select (-1 to deselect)

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#uielement-getselectedelement-const","title":"UIElement* getSelectedElement() const","text":"

    Gets the selected element.

    Returns: - UIElement*: Pointer to selected element, or nullptr if none selected

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setscrollspeedfloat-speed","title":"void setScrollSpeed(float speed)","text":"

    Sets the scroll speed for smooth scrolling.

    Parameters: - speed (float): Pixels per millisecond

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setnavigationbuttonsuint8_t-leftbutton-uint8_t-rightbutton","title":"void setNavigationButtons(uint8_t leftButton, uint8_t rightButton)","text":"

    Sets the navigation button indices.

    Parameters: - leftButton (uint8_t): Button index for LEFT navigation - rightButton (uint8_t): Button index for RIGHT navigation

    Returns: - void

    Notes: - Default: LEFT = 2, RIGHT = 3 - Change if your input mapping differs

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setbuttonstylecolor-selectedtextcol-color-selectedbgcol-color-unselectedtextcol-color-unselectedbgcol","title":"void setButtonStyle(Color selectedTextCol, Color selectedBgCol, Color unselectedTextCol, Color unselectedBgCol)","text":"

    Sets the style colors for selected and unselected buttons.

    Parameters: - selectedTextCol (Color): Text color when selected - selectedBgCol (Color): Background color when selected - unselectedTextCol (Color): Text color when not selected - unselectedBgCol (Color): Background color when not selected

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIHorizontalLayout.h\"\n\nclass ToolbarScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIHorizontalLayout* toolbar;\n\npublic:\n    void init() override {\n        // Create horizontal toolbar\n        toolbar = new pixelroot32::graphics::ui::UIHorizontalLayout(\n            0.0f, 0.0f,    // Top of screen\n            128.0f, 20.0f  // Full width, 20px tall\n        );\n        toolbar->setSpacing(4.0f);\n        toolbar->setPadding(2.0f);\n\n        // Add toolbar buttons\n        toolbar->addElement(newButton(\"File\", ...));\n        toolbar->addElement(newButton(\"Edit\", ...));\n        toolbar->addElement(newButton(\"View\", ...));\n\n        addEntity(toolbar);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#see-also","title":"See Also","text":"
    • UILayout - Base layout class (abstract)
    • UIVerticalLayout - Vertical layout
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/padding_container/","title":"UIPaddingContainer","text":"

    Container that wraps a single UI element and applies padding.

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#description","title":"Description","text":"

    UIPaddingContainer adds padding/margin around a single child element without organizing multiple elements. Useful for adding spacing to individual elements or nesting layouts with custom padding.

    This container is simpler than UIPanel (no background/border) and focuses only on spacing.

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIPaddingContainer : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    • Inherited by: Your custom padding containers (if needed)
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/padding_container/#uipaddingcontainerfloat-x-float-y-float-w-float-h","title":"UIPaddingContainer(float x, float y, float w, float h)","text":"

    Constructs a new UIPaddingContainer.

    Parameters: - x (float): X position of the container - y (float): Y position of the container - w (float): Width of the container - h (float): Height of the container

    Example:

    #include \"graphics/ui/UIPaddingContainer.h\"\n\n// Create padding container\npixelroot32::graphics::ui::UIPaddingContainer* padded = \n    new pixelroot32::graphics::ui::UIPaddingContainer(\n        10.0f, 10.0f,\n        108.0f, 108.0f\n    );\npadded->setPadding(8.0f);  // 8px padding on all sides\n

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/padding_container/#void-setchilduielement-element","title":"void setChild(UIElement* element)","text":"

    Sets the child element.

    Parameters: - element (UIElement*): Pointer to the UI element to wrap

    Returns: - void

    Notes: - Child element is positioned with padding applied - Can wrap any UI element (button, label, layout, etc.)

    Example:

    padded->setChild(button);\n

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#uielement-getchild-const","title":"UIElement* getChild() const","text":"

    Gets the child element.

    Returns: - UIElement*: Pointer to the child element, or nullptr if none set

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#void-setpaddingfloat-p","title":"void setPadding(float p)","text":"

    Sets uniform padding on all sides.

    Parameters: - p (float): Padding value in pixels

    Returns: - void

    Notes: - Applies same padding to all sides - Child position is automatically updated

    Example:

    padded->setPadding(10.0f);  // 10px padding all around\n

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#void-setpaddingfloat-left-float-right-float-top-float-bottom","title":"void setPadding(float left, float right, float top, float bottom)","text":"

    Sets asymmetric padding.

    Parameters: - left (float): Left padding in pixels - right (float): Right padding in pixels - top (float): Top padding in pixels - bottom (float): Bottom padding in pixels

    Returns: - void

    Example:

    padded->setPadding(10.0f, 5.0f, 8.0f, 12.0f);  // Different padding per side\n

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#float-getpaddingleft-const","title":"float getPaddingLeft() const","text":"

    Gets the left padding.

    Returns: - float: Left padding in pixels

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#float-getpaddingright-const","title":"float getPaddingRight() const","text":"

    Gets the right padding.

    Returns: - float: Right padding in pixels

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#float-getpaddingtop-const","title":"float getPaddingTop() const","text":"

    Gets the top padding.

    Returns: - float: Top padding in pixels

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#float-getpaddingbottom-const","title":"float getPaddingBottom() const","text":"

    Gets the bottom padding.

    Returns: - float: Bottom padding in pixels

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#void-setpositionfloat-newx-float-newy","title":"void setPosition(float newX, float newY)","text":"

    Sets the position of the container. Also updates the child element's position.

    Parameters: - newX (float): New X coordinate - newY (float): New Y coordinate

    Returns: - void

    Notes: - Child element position is updated automatically with padding applied

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the container and child element.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    Notes: - Updates child element if set - Called automatically by Scene

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws the child element.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    Notes: - Only draws child element (no background/border) - Child is drawn at padded position

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIPaddingContainer.h\"\n\nclass MenuScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Create button\n        auto* button = new UIButton(\"Start\", 0, 0, 0, 100.0f, 30.0f, \n                                    [this]() { startGame(); });\n\n        // Wrap button with padding\n        auto* paddedButton = new pixelroot32::graphics::ui::UIPaddingContainer(\n            64.0f, 50.0f,  // position\n            120.0f, 50.0f  // size (button + padding)\n        );\n        paddedButton->setPadding(10.0f);  // 10px padding\n        paddedButton->setChild(button);\n\n        addEntity(paddedButton);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#nesting-with-layouts","title":"Nesting with Layouts","text":"
    // Create layout\nauto* layout = new UIVerticalLayout(10, 10, 108, 108);\n\n// Wrap layout with padding\nauto* paddedLayout = new UIPaddingContainer(0, 0, 128, 128);\npaddedLayout->setPadding(10.0f, 10.0f, 20.0f, 20.0f);  // Asymmetric\npaddedLayout->setChild(layout);\n
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#performance-considerations","title":"Performance Considerations","text":"
    • Rendering: Very efficient (just draws child)
    • Position calculation: Fast (simple addition)
    • Memory: Minimal overhead
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Very lightweight
    • Update frequency: Position only recalculates when padding/position changes
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#see-also","title":"See Also","text":"
    • UIElement - Base UI element class
    • UIPanel - Panel with background and border
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/panel/","title":"UIPanel","text":"

    Visual container that draws a background and border around a child element.

    "},{"location":"api_reference/ui/ui_layouts/panel/#description","title":"Description","text":"

    UIPanel provides a retro-style window/panel appearance with a background color and border. Typically contains a UILayout or other UI elements. Useful for dialogs, menus, and information panels.

    The panel wraps a single child element and draws a background rectangle and border around it.

    "},{"location":"api_reference/ui/ui_layouts/panel/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIPanel : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/panel/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    • Inherited by: Your custom panel classes (if needed)
    "},{"location":"api_reference/ui/ui_layouts/panel/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/panel/#uipanelfloat-x-float-y-float-w-float-h","title":"UIPanel(float x, float y, float w, float h)","text":"

    Constructs a new UIPanel.

    Parameters: - x (float): X position of the panel - y (float): Y position of the panel - w (float): Width of the panel - h (float): Height of the panel

    Example:

    #include \"graphics/ui/UIPanel.h\"\n\n// Create dialog panel\npixelroot32::graphics::ui::UIPanel* dialog = \n    new pixelroot32::graphics::ui::UIPanel(\n        20.0f, 30.0f,  // position\n        88.0f, 68.0f   // size\n    );\n

    "},{"location":"api_reference/ui/ui_layouts/panel/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/panel/#void-setchilduielement-element","title":"void setChild(UIElement* element)","text":"

    Sets the child element.

    Parameters: - element (UIElement*): Pointer to the UI element to wrap (typically a UILayout)

    Returns: - void

    Notes: - Child element is positioned inside the panel (with padding) - Typically a layout (VerticalLayout, etc.)

    Example:

    // Create panel\nauto* panel = new UIPanel(20, 30, 88, 68);\n\n// Create layout for panel content\nauto* layout = new UIVerticalLayout(0, 0, 80, 60);\nlayout->addElement(button1);\nlayout->addElement(button2);\n\n// Set layout as panel child\npanel->setChild(layout);\n

    "},{"location":"api_reference/ui/ui_layouts/panel/#uielement-getchild-const","title":"UIElement* getChild() const","text":"

    Gets the child element.

    Returns: - UIElement*: Pointer to the child element, or nullptr if none set

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-setbackgroundcolorcolor-color","title":"void setBackgroundColor(Color color)","text":"

    Sets the background color.

    Parameters: - color (Color): Background color

    Returns: - void

    Example:

    panel->setBackgroundColor(Color::Blue);\n

    "},{"location":"api_reference/ui/ui_layouts/panel/#color-getbackgroundcolor-const","title":"Color getBackgroundColor() const","text":"

    Gets the background color.

    Returns: - Color: Background color

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-setbordercolorcolor-color","title":"void setBorderColor(Color color)","text":"

    Sets the border color.

    Parameters: - color (Color): Border color

    Returns: - void

    Example:

    panel->setBorderColor(Color::White);\n

    "},{"location":"api_reference/ui/ui_layouts/panel/#color-getbordercolor-const","title":"Color getBorderColor() const","text":"

    Gets the border color.

    Returns: - Color: Border color

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-setborderwidthuint8_t-width","title":"void setBorderWidth(uint8_t width)","text":"

    Sets the border width.

    Parameters: - width (uint8_t): Border width in pixels

    Returns: - void

    Notes: - Default: 1 pixel - Higher values = thicker border

    Example:

    panel->setBorderWidth(2);  // 2 pixel border\n

    "},{"location":"api_reference/ui/ui_layouts/panel/#uint8_t-getborderwidth-const","title":"uint8_t getBorderWidth() const","text":"

    Gets the border width.

    Returns: - uint8_t: Border width in pixels

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-setpositionfloat-newx-float-newy","title":"void setPosition(float newX, float newY)","text":"

    Sets the position of the panel. Also updates the child element's position.

    Parameters: - newX (float): New X coordinate - newY (float): New Y coordinate

    Returns: - void

    Notes: - Child element position is updated automatically

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the panel and child element.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    Notes: - Updates child element if set - Called automatically by Scene

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws the panel (background, border) and child element.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    Notes: - Draws background rectangle - Draws border rectangle - Draws child element if set

    "},{"location":"api_reference/ui/ui_layouts/panel/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIPanel.h\"\n#include \"graphics/ui/UIVerticalLayout.h\"\n\nclass DialogScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIPanel* dialog;\n\npublic:\n    void init() override {\n        // Create dialog panel\n        dialog = new pixelroot32::graphics::ui::UIPanel(\n            20.0f, 30.0f,  // position\n            88.0f, 68.0f   // size\n        );\n        dialog->setBackgroundColor(Color::Navy);\n        dialog->setBorderColor(Color::White);\n        dialog->setBorderWidth(2);\n\n        // Create layout for dialog content\n        auto* layout = new pixelroot32::graphics::ui::UIVerticalLayout(\n            4.0f, 4.0f,  // Position inside panel\n            80.0f, 60.0f // Size inside panel\n        );\n        layout->setSpacing(8.0f);\n\n        // Add buttons\n        auto* okButton = new UIButton(\"OK\", 0, 0, 0, 70.0f, 20.0f, \n                                     [this]() { closeDialog(); });\n        auto* cancelButton = new UIButton(\"Cancel\", 1, 0, 0, 70.0f, 20.0f,\n                                         [this]() { closeDialog(); });\n\n        layout->addElement(okButton);\n        layout->addElement(cancelButton);\n\n        // Set layout as panel child\n        dialog->setChild(layout);\n\n        addEntity(dialog);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/panel/#performance-considerations","title":"Performance Considerations","text":"
    • Rendering: Simple rectangles; very efficient
    • Child updates: Child element updates are fast
    • Memory: Small overhead (just colors and border width)
    "},{"location":"api_reference/ui/ui_layouts/panel/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Panel is lightweight
    • Rendering: Two rectangles (background + border); minimal overhead
    "},{"location":"api_reference/ui/ui_layouts/panel/#see-also","title":"See Also","text":"
    • UIElement - Base UI element class
    • UILayouts - Layout containers to use inside panels
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/","title":"UIVerticalLayout","text":"

    Vertical layout container with scroll support.

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#description","title":"Description","text":"

    UIVerticalLayout organizes UI elements vertically, one below another. It supports scrolling when content exceeds the visible viewport and handles keyboard/D-pad navigation automatically.

    This layout is ideal for menus, lists, and any vertical arrangement of UI elements.

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIVerticalLayout : public UILayout {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#inheritance","title":"Inheritance","text":"
    • Inherits from: UILayout
    • Inherited by: Your custom vertical layouts (if needed)
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/vertical_layout/#uiverticallayoutfloat-x-float-y-float-w-float-h","title":"UIVerticalLayout(float x, float y, float w, float h)","text":"

    Constructs a new UIVerticalLayout.

    Parameters: - x (float): X position of the layout container - y (float): Y position of the layout container - w (float): Width of the layout container - h (float): Height of the layout container (viewport height)

    Example:

    #include \"graphics/ui/UIVerticalLayout.h\"\n\n// Create vertical layout for menu\npixelroot32::graphics::ui::UIVerticalLayout* menuLayout = \n    new pixelroot32::graphics::ui::UIVerticalLayout(\n        20.0f, 20.0f,  // position\n        88.0f, 88.0f   // size (viewport)\n    );\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-addelementuielement-element","title":"void addElement(UIElement* element)","text":"

    Adds a UI element to the layout.

    Parameters: - element (UIElement*): Pointer to the element to add

    Returns: - void

    Notes: - Elements are arranged vertically, one below another - Layout is automatically recalculated - Elements are positioned based on spacing and padding

    Example:

    menuLayout->addElement(startButton);\nmenuLayout->addElement(quitButton);\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-removeelementuielement-element","title":"void removeElement(UIElement* element)","text":"

    Removes a UI element from the layout.

    Parameters: - element (UIElement*): Pointer to the element to remove

    Returns: - void

    Notes: - Layout is automatically recalculated - Element is not deleted (you must manage its lifetime)

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-updatelayout","title":"void updateLayout()","text":"

    Recalculates positions of all elements.

    Returns: - void

    Notes: - Called automatically when elements are added/removed - Can be called manually if needed - Recalculates all element positions and content height

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-handleinputconst-inputmanager-input","title":"void handleInput(const InputManager& input)","text":"

    Handles input for navigation and scrolling.

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    Returns: - void

    Notes: - Handles UP/DOWN navigation - Manages selection state - Handles scrolling if enabled - Should be called every frame in update()

    Example:

    void update(unsigned long deltaTime) override {\n    UIVerticalLayout::update(deltaTime);\n\n    auto& input = engine.getInputManager();\n    menuLayout->handleInput(input);\n}\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the layout (handles smooth scrolling).

    Parameters: - deltaTime (unsigned long): Time elapsed since last frame in milliseconds

    Returns: - void

    Notes: - Called automatically by Scene if isEnabled is true - Updates smooth scrolling animation - Updates child elements

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws the layout and its visible elements.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    Notes: - Called automatically by Scene if isVisible is true - Only draws visible elements (viewport culling) - Draws elements in order

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setscrollenabledbool-enable","title":"void setScrollEnabled(bool enable)","text":"

    Enables or disables scrolling.

    Parameters: - enable (bool): true to enable scrolling

    Returns: - void

    Notes: - When disabled, scroll offset is reset to 0 - Scrolling is useful when content exceeds viewport height

    Example:

    menuLayout->setScrollEnabled(true);  // Enable scrolling\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-enablescrollbool-enable","title":"void enableScroll(bool enable)","text":"

    Alias for setScrollEnabled().

    Parameters: - enable (bool): true to enable scrolling

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setviewportheightfloat-h","title":"void setViewportHeight(float h)","text":"

    Sets the viewport height (visible area).

    Parameters: - h (float): Viewport height in pixels

    Returns: - void

    Notes: - Layout is automatically recalculated - Use to adjust visible area

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#float-getscrolloffset-const","title":"float getScrollOffset() const","text":"

    Gets the current scroll offset.

    Returns: - float: Scroll offset in pixels

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setscrolloffsetfloat-offset","title":"void setScrollOffset(float offset)","text":"

    Sets the scroll offset directly.

    Parameters: - offset (float): Scroll offset in pixels

    Returns: - void

    Notes: - Offset is clamped to valid range automatically - Use for programmatic scrolling

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#float-getcontentheight-const","title":"float getContentHeight() const","text":"

    Gets the total content height.

    Returns: - float: Content height in pixels

    Notes: - Includes all elements plus spacing and padding - Useful for scroll calculations

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#int-getselectedindex-const","title":"int getSelectedIndex() const","text":"

    Gets the currently selected element index.

    Returns: - int: Selected index, or -1 if none selected

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setselectedindexint-index","title":"void setSelectedIndex(int index)","text":"

    Sets the selected element index.

    Parameters: - index (int): Index to select (-1 to deselect)

    Returns: - void

    Notes: - Selected element is highlighted - Selection is scrolled into view if needed

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#uielement-getselectedelement-const","title":"UIElement* getSelectedElement() const","text":"

    Gets the selected element.

    Returns: - UIElement*: Pointer to selected element, or nullptr if none selected

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setscrollspeedfloat-speed","title":"void setScrollSpeed(float speed)","text":"

    Sets the scroll speed for smooth scrolling.

    Parameters: - speed (float): Pixels per millisecond

    Returns: - void

    Notes: - Default: 0.5 pixels per millisecond - Higher values = faster scrolling

    Example:

    menuLayout->setScrollSpeed(1.0f);  // Faster scrolling\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setnavigationbuttonsuint8_t-upbutton-uint8_t-downbutton","title":"void setNavigationButtons(uint8_t upButton, uint8_t downButton)","text":"

    Sets the navigation button indices.

    Parameters: - upButton (uint8_t): Button index for UP navigation - downButton (uint8_t): Button index for DOWN navigation

    Returns: - void

    Notes: - Default: UP = 0, DOWN = 1 - Change if your input mapping differs

    Example:

    menuLayout->setNavigationButtons(0, 1);  // UP=0, DOWN=1\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setbuttonstylecolor-selectedtextcol-color-selectedbgcol-color-unselectedtextcol-color-unselectedbgcol","title":"void setButtonStyle(Color selectedTextCol, Color selectedBgCol, Color unselectedTextCol, Color unselectedBgCol)","text":"

    Sets the style colors for selected and unselected buttons.

    Parameters: - selectedTextCol (Color): Text color when selected - selectedBgCol (Color): Background color when selected - unselectedTextCol (Color): Text color when not selected - unselectedBgCol (Color): Background color when not selected

    Returns: - void

    Example:

    menuLayout->setButtonStyle(\n    Color::Yellow,  // Selected text\n    Color::Blue,    // Selected background\n    Color::White,   // Unselected text\n    Color::Black    // Unselected background\n);\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIVerticalLayout.h\"\n#include \"graphics/ui/UIButton.h\"\n\nclass MainMenuScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIVerticalLayout* menuLayout;\n\npublic:\n    void init() override {\n        // Create menu layout\n        menuLayout = new pixelroot32::graphics::ui::UIVerticalLayout(\n            20.0f, 20.0f,  // position\n            88.0f, 88.0f   // size\n        );\n        menuLayout->setScrollEnabled(true);\n        menuLayout->setSpacing(8.0f);\n        menuLayout->setPadding(4.0f);\n\n        // Create buttons\n        auto* startButton = new pixelroot32::graphics::ui::UIButton(\n            \"Start\",\n            0, 64.0f, 50.0f, 100.0f, 30.0f,\n            [this]() { engine.setScene(&gameScene); }\n        );\n\n        auto* quitButton = new pixelroot32::graphics::ui::UIButton(\n            \"Quit\",\n            1, 64.0f, 50.0f, 100.0f, 30.0f,\n            [this]() { engine.stop(); }\n        );\n\n        // Add buttons to layout\n        menuLayout->addElement(startButton);\n        menuLayout->addElement(quitButton);\n\n        // Add layout to scene\n        addEntity(menuLayout);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Layout handles input automatically\n        auto& input = engine.getInputManager();\n        menuLayout->handleInput(input);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(0, 0, 128, 128, Color::Black);\n        Scene::draw(renderer);  // Draws layout and buttons\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#navigation","title":"Navigation","text":"

    The layout handles D-pad navigation automatically:

    • UP button: Moves selection up
    • DOWN button: Moves selection down
    • Action button: Triggers selected button's callback
    • Scrolling: Automatically scrolls to keep selected element visible
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#performance-considerations","title":"Performance Considerations","text":"
    • Viewport culling: Only visible elements are drawn
    • Layout recalculation: Fast (simple positioning)
    • Scrolling: Smooth scrolling is efficient
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Element count: Stay within MAX_ENTITIES limit
    • Scrolling: Smooth scrolling uses minimal CPU
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#see-also","title":"See Also","text":"
    • UILayout - Base layout class (abstract)
    • UIButton - Buttons for menus
    • Manual - User Interface
    • API Overview
    "},{"location":"examples/space_invaders/overview/","title":"Space Invaders Example","text":"

    A complete implementation of the classic Space Invaders game showcasing advanced PixelRoot32 features including sprite animations, collision detection with sweep tests, dynamic music tempo, tilemap backgrounds, and efficient entity management.

    "},{"location":"examples/space_invaders/overview/#overview","title":"Overview","text":"

    This example demonstrates a fully playable Space Invaders game with: - Player ship with horizontal movement and shooting - Formation of 32 aliens (4 rows \u00d7 8 columns) with different types - Defensive bunkers that degrade when hit - Dynamic background music that speeds up as aliens approach - Visual explosion effects - Score and lives system - Win/lose conditions

    "},{"location":"examples/space_invaders/overview/#architecture","title":"Architecture","text":""},{"location":"examples/space_invaders/overview/#scene-structure","title":"Scene Structure","text":"

    SpaceInvadersScene (SpaceInvadersScene.h/cpp) - Main game scene managing all entities and game state - Handles game loop, collisions, spawning, and state transitions - Implements custom entity management to work within MAX_ENTITIES limit

    "},{"location":"examples/space_invaders/overview/#entity-classes","title":"Entity Classes","text":""},{"location":"examples/space_invaders/overview/#playeractor","title":"PlayerActor","text":"
    • Type: PhysicsActor
    • Features:
    • Horizontal movement controlled by LEFT/RIGHT buttons
    • Shooting with FIRE button
    • Physics-based movement with world boundaries
    • Respawn system after being hit
    "},{"location":"examples/space_invaders/overview/#alienactor","title":"AlienActor","text":"
    • Type: Actor
    • Features:
    • Three types: SQUID (top row, 30 points), CRAB (middle rows, 20 points), OCTOPUS (bottom rows, 10 points)
    • Step-based sprite animations (alternates frames on movement)
    • Formation movement (moves as a group, drops down when hitting edges)
    • Uses MultiSprite for CRAB type (multi-layer sprite)
    "},{"location":"examples/space_invaders/overview/#projectileactor","title":"ProjectileActor","text":"
    • Type: PhysicsActor
    • Features:
    • Two types: PLAYER_BULLET (white, moves up) and ENEMY_BULLET (red, moves down)
    • Object pooling (reuses projectiles instead of creating/destroying)
    • Sweep test collision detection for fast-moving projectiles
    • Tracks previous position for accurate collision detection
    "},{"location":"examples/space_invaders/overview/#bunkeractor","title":"BunkerActor","text":"
    • Type: Actor
    • Features:
    • Health system (starts at 4, decreases when hit)
    • Visual degradation (color changes: Green \u2192 Yellow \u2192 Red)
    • Collision detection with dynamic hitbox based on remaining health
    "},{"location":"examples/space_invaders/overview/#background","title":"Background","text":"

    TilemapBackground - Starfield created using tilemap system - Procedurally generated pattern - Rendered on layer 0 (background)

    "},{"location":"examples/space_invaders/overview/#key-systems","title":"Key Systems","text":""},{"location":"examples/space_invaders/overview/#collision-detection","title":"Collision Detection","text":"

    The game uses sweep tests for accurate collision detection with fast-moving projectiles:

    // Example from SpaceInvadersScene::handleCollisions()\nCircle startCircle;\nstartCircle.x = proj->getPreviousX() + radius;\nstartCircle.y = proj->getPreviousY() + PROJECTILE_HEIGHT * 0.5f;\nstartCircle.radius = radius;\n\nCircle endCircle;\nendCircle.x = proj->x + radius;\nendCircle.y = proj->y + PROJECTILE_HEIGHT * 0.5f;\nendCircle.radius = radius;\n\nfloat tHit = 0.0f;\nif (sweepCircleVsRect(startCircle, endCircle, targetBox, tHit)) {\n    // Collision detected\n}\n

    Why sweep tests? - Projectiles move fast (120 px/s) - Standard AABB can miss collisions between frames - Sweep tests check the path between previous and current position

    "},{"location":"examples/space_invaders/overview/#entity-management","title":"Entity Management","text":"

    Due to the MAX_ENTITIES = 32 limit, the scene uses custom entity management:

    // Custom vectors instead of Scene's entity system\nstd::vector<AlienActor*> aliens;\nstd::vector<ProjectileActor*> projectiles;\nstd::vector<BunkerActor*> bunkers;\n

    Object Pooling: - Projectiles are pre-allocated (MaxProjectiles = 12) - Projectiles are reused instead of created/destroyed - reset() method reactivates projectiles

    Scene Arena (Optional): - Uses PIXELROOT32_ENABLE_SCENE_ARENA if available - Pre-allocates memory for all entities - Avoids heap fragmentation

    "},{"location":"examples/space_invaders/overview/#dynamic-music-tempo","title":"Dynamic Music Tempo","text":"

    The background music tempo increases as aliens get closer to the player:

    void SpaceInvadersScene::updateMusicTempo() {\n    // Find lowest active alien\n    float lowestY = /* ... */;\n\n    // Calculate threat factor (0.0 to 1.0)\n    float threatFactor = (lowestY - ALIEN_START_Y) * INV_Y_RANGE;\n\n    // Target tempo: 1.0 (slow) to 1.9 (fast)\n    float targetTempo = 1.0f + (threatFactor * 0.9f);\n\n    // Smooth interpolation\n    currentMusicTempoFactor += (targetTempo - currentMusicTempoFactor) * 0.05f;\n\n    engine.getMusicPlayer().setTempoFactor(currentMusicTempoFactor);\n}\n

    Music Tracks: - BGM_SLOW_TRACK: Initial tempo (slow) - BGM_MEDIUM_TRACK: Medium tempo (unused, can be used for transitions) - BGM_FAST_TRACK: Fast tempo (unused, can be used for transitions) - WIN_TRACK: Victory music (plays once) - GAME_OVER_TRACK: Defeat music (plays once)

    "},{"location":"examples/space_invaders/overview/#visual-effects","title":"Visual Effects","text":""},{"location":"examples/space_invaders/overview/#enemy-explosions","title":"Enemy Explosions","text":"
    • Simple cross pattern (horizontal + vertical lines)
    • Pool of 8 explosion effects
    • 200ms duration each
    • Reused slots to avoid allocations
    "},{"location":"examples/space_invaders/overview/#player-explosion","title":"Player Explosion","text":"
    • 3-frame sprite animation
    • Plays when player is hit
    • Pauses gameplay during animation
    • Respawns player after animation completes
    "},{"location":"examples/space_invaders/overview/#alien-formation-movement","title":"Alien Formation Movement","text":"

    Aliens move in lockstep formation:

    void SpaceInvadersScene::updateAliens(unsigned long deltaTime) {\n    stepTimer += scaledDelta;\n\n    if (stepTimer >= stepDelay) {\n        // Check if formation hit edge\n        bool edgeHit = /* ... */;\n\n        if (edgeHit) {\n            moveDirection *= -1; // Reverse direction\n            // Drop down\n            for (auto* alien : aliens) {\n                alien->move(0, ALIEN_DROP_AMOUNT);\n            }\n        } else {\n            // Move horizontally\n            float dx = moveDirection * ALIEN_STEP_AMOUNT_X;\n            for (auto* alien : aliens) {\n                alien->move(dx, 0);\n            }\n        }\n    }\n}\n

    Movement Characteristics: - Step-based (not continuous) - Base step delay: 417ms (72 BPM) - Step amount: 2.5 pixels horizontally - Drop amount: 7 pixels when hitting edge - Tempo scales with music tempo factor

    "},{"location":"examples/space_invaders/overview/#enemy-shooting-system","title":"Enemy Shooting System","text":"

    Enemies shoot from bottom-most aliens:

    void SpaceInvadersScene::enemyShoot() {\n    // Find bottom-most aliens (no alien below them)\n    std::vector<AlienActor*> bottomAliens;\n    // ... collect bottom aliens ...\n\n    // Difficulty-based chance (increases as aliens die)\n    float t = 1.0f - (alive / total);\n    int chance = minChance + (t * (maxChance - minChance));\n\n    // Random roll\n    if (roll >= chance) {\n        // Fire from random bottom alien\n    }\n}\n

    Shooting Rules: - Only bottom-most aliens can shoot - Maximum 4 enemy bullets active at once - Chance increases as more aliens are destroyed - Difficulty scales from 8% to 30% chance

    "},{"location":"examples/space_invaders/overview/#code-structure","title":"Code Structure","text":""},{"location":"examples/space_invaders/overview/#file-organization","title":"File Organization","text":"
    SpaceInvaders/\n\u251c\u2500\u2500 SpaceInvadersScene.h/cpp    # Main scene\n\u251c\u2500\u2500 PlayerActor.h/cpp           # Player ship\n\u251c\u2500\u2500 AlienActor.h/cpp            # Enemy aliens\n\u251c\u2500\u2500 ProjectileActor.h/cpp       # Bullets\n\u251c\u2500\u2500 BunkerActor.h/cpp           # Defensive bunkers\n\u251c\u2500\u2500 GameConstants.h             # Game configuration\n\u2514\u2500\u2500 AlienSprites.h              # Sprite definitions\n
    "},{"location":"examples/space_invaders/overview/#game-constants","title":"Game Constants","text":"

    Key constants defined in GameConstants.h:

    // Formation\nconstexpr int ALIEN_ROWS = 4;\nconstexpr int ALIEN_COLS = 8;\n\n// Movement\nconstexpr unsigned long BASE_STEP_DELAY = 417; // 72 BPM\nconstexpr float ALIEN_STEP_AMOUNT_X = 2.5f;\nconstexpr float ALIEN_DROP_AMOUNT = 7.0f;\n\n// Projectiles\nconstexpr int MaxProjectiles = 12;\nconstexpr float PROJECTILE_SPEED = 120.0f;\n\n// Bunkers\nconstexpr int BUNKER_COUNT = 4;\nconstexpr int BUNKER_WIDTH = 24;\nconstexpr int BUNKER_HEIGHT = 16;\n
    "},{"location":"examples/space_invaders/overview/#design-patterns-used","title":"Design Patterns Used","text":""},{"location":"examples/space_invaders/overview/#1-object-pooling","title":"1. Object Pooling","text":"

    Projectiles are pooled to avoid allocations:

    // Pre-allocate in resetGame()\nfor (int i = 0; i < MaxProjectiles; ++i) {\n    ProjectileActor* projectile = new ProjectileActor(0, -PROJECTILE_HEIGHT, ProjectileType::PLAYER_BULLET);\n    projectile->deactivate();\n    projectiles.push_back(projectile);\n}\n\n// Reuse when shooting\nfor (auto* proj : projectiles) {\n    if (!proj->isActive()) {\n        proj->reset(px, py, ProjectileType::PLAYER_BULLET);\n        break;\n    }\n}\n
    "},{"location":"examples/space_invaders/overview/#2-state-machine","title":"2. State Machine","text":"

    Game states: Playing \u2192 Paused (on hit) \u2192 Game Over

    if (gameOver) {\n    // Game over state\n} else if (isPaused) {\n    // Paused during player explosion\n    if (!playerExplosion.isActive()) {\n        respawnPlayerUnderBunker();\n        isPaused = false;\n    }\n} else {\n    // Normal gameplay\n}\n
    "},{"location":"examples/space_invaders/overview/#3-formation-controller","title":"3. Formation Controller","text":"

    Aliens are moved as a group by the scene, not individually:

    // Scene controls movement\nfor (auto* alien : aliens) {\n    if (alien->isActive()) {\n        alien->move(dx, 0); // Scene tells alien to move\n    }\n}\n
    "},{"location":"examples/space_invaders/overview/#4-explosion-pool","title":"4. Explosion Pool","text":"

    Enemy explosions use a fixed pool:

    static constexpr int MaxEnemyExplosions = 8;\nEnemyExplosion enemyExplosions[MaxEnemyExplosions];\n\nvoid spawnEnemyExplosion(float x, float y) {\n    // Find first available slot\n    for (int i = 0; i < MaxEnemyExplosions; ++i) {\n        if (!enemyExplosions[i].active) {\n            enemyExplosions[i].active = true;\n            enemyExplosions[i].x = x;\n            enemyExplosions[i].y = y;\n            enemyExplosions[i].remainingMs = 200;\n            return;\n        }\n    }\n}\n
    "},{"location":"examples/space_invaders/overview/#audio-integration","title":"Audio Integration","text":""},{"location":"examples/space_invaders/overview/#sound-effects","title":"Sound Effects","text":"

    Player Shoot:

    AudioEvent event{};\nevent.type = WaveType::PULSE;\nevent.frequency = 880.0f;\nevent.duration = 0.08f;\nevent.volume = 0.4f;\nevent.duty = 0.5f;\nengine.getAudioEngine().playEvent(event);\n

    Enemy Hit:

    AudioEvent event{};\nevent.type = WaveType::NOISE;\nevent.frequency = 600.0f;\nevent.duration = 0.12f;\nevent.volume = 0.6f;\nengine.getAudioEngine().playEvent(event);\n

    Player Hit:

    AudioEvent event{};\nevent.type = WaveType::NOISE;\nevent.frequency = 400.0f;\nevent.duration = 0.18f;\nevent.volume = 0.7f;\nengine.getAudioEngine().playEvent(event);\n

    "},{"location":"examples/space_invaders/overview/#background-music","title":"Background Music","text":"
    • Uses MusicPlayer with tempo control
    • Tempo dynamically adjusts based on threat level
    • Different tracks for win/lose conditions
    "},{"location":"examples/space_invaders/overview/#sprite-system","title":"Sprite System","text":""},{"location":"examples/space_invaders/overview/#sprite-formats","title":"Sprite Formats","text":"
    • 1bpp sprites: Player, most aliens, explosions
    • MultiSprite: CRAB alien type (multi-layer sprite)
    • Animations: Step-based (advances on movement, not time)
    "},{"location":"examples/space_invaders/overview/#sprite-scaling","title":"Sprite Scaling","text":"

    All sprites use SPRITE_SCALE = 1.25f for larger appearance:

    renderer.drawSprite(PLAYER_SHIP_SPRITE,\n    static_cast<int>(x),\n    static_cast<int>(y),\n    SPRITE_SCALE,  // scaleX\n    SPRITE_SCALE,  // scaleY\n    Color::White);\n
    "},{"location":"examples/space_invaders/overview/#performance-optimizations","title":"Performance Optimizations","text":""},{"location":"examples/space_invaders/overview/#1-entity-pooling","title":"1. Entity Pooling","text":"
    • Projectiles are pooled (12 max)
    • Explosions are pooled (8 max)
    • Avoids allocations during gameplay
    "},{"location":"examples/space_invaders/overview/#2-scene-arena-optional","title":"2. Scene Arena (Optional)","text":"
    • Pre-allocates 8KB buffer for entities
    • All entities allocated from arena
    • Zero heap fragmentation
    "},{"location":"examples/space_invaders/overview/#3-efficient-collision-detection","title":"3. Efficient Collision Detection","text":"
    • Sweep tests only for fast projectiles
    • Simple AABB for static/slow objects
    • Early exits in collision loops
    "},{"location":"examples/space_invaders/overview/#4-custom-entity-management","title":"4. Custom Entity Management","text":"
    • Bypasses Scene's entity limit by using vectors
    • Direct control over entity lifecycle
    • More efficient for this specific use case
    "},{"location":"examples/space_invaders/overview/#game-flow","title":"Game Flow","text":""},{"location":"examples/space_invaders/overview/#initialization","title":"Initialization","text":"
    1. Create tilemap background
    2. Spawn player at bottom center
    3. Spawn 32 aliens in formation (4\u00d78 grid)
    4. Spawn 4 bunkers evenly spaced
    5. Pre-allocate projectile pool
    6. Start background music
    "},{"location":"examples/space_invaders/overview/#gameplay-loop","title":"Gameplay Loop","text":"
    1. Update Input: Read player movement and shooting
    2. Update Aliens: Move formation, check edges, enemy shooting
    3. Update Projectiles: Move projectiles, check boundaries
    4. Handle Collisions: Sweep tests for projectiles vs aliens/bunkers/player
    5. Update Effects: Explosion animations
    6. Update Music: Adjust tempo based on alien position
    7. Draw: Background, entities, explosions, HUD
    "},{"location":"examples/space_invaders/overview/#game-over-conditions","title":"Game Over Conditions","text":"
    • Win: All aliens destroyed
    • Lose: Player lives reach 0 OR aliens reach player Y position
    "},{"location":"examples/space_invaders/overview/#respawn-system","title":"Respawn System","text":"
    1. Player hit \u2192 Start explosion animation
    2. Pause gameplay
    3. Wait for explosion to complete
    4. Respawn player under first intact bunker
    5. Resume gameplay
    "},{"location":"examples/space_invaders/overview/#key-learnings","title":"Key Learnings","text":""},{"location":"examples/space_invaders/overview/#what-this-example-demonstrates","title":"What This Example Demonstrates","text":"
    1. Advanced Collision Detection
    2. Sweep tests for fast-moving objects
    3. Accurate hit detection even at high speeds

    4. Efficient Entity Management

    5. Object pooling for frequently created/destroyed entities
    6. Custom management to work around MAX_ENTITIES limit
    7. Scene Arena for zero-fragmentation allocation

    8. Dynamic Audio

    9. Music tempo synchronization with gameplay
    10. Multiple music tracks for different states
    11. Sound effects for game events

    12. Sprite Animations

    13. Step-based animations (tied to game logic, not time)
    14. MultiSprite for complex sprites
    15. Sprite scaling for visual variety

    16. State Management

    17. Game states (playing, paused, game over)
    18. Smooth transitions between states
    19. Respawn system with visual feedback

    20. Formation Movement

    21. Coordinated group movement
    22. Edge detection and direction reversal
    23. Progressive difficulty (tempo increases)

    24. Visual Effects

    25. Explosion animations
    26. Health-based rendering (bunkers)
    27. HUD with score and lives
    "},{"location":"examples/space_invaders/overview/#code-examples","title":"Code Examples","text":""},{"location":"examples/space_invaders/overview/#shooting-system","title":"Shooting System","text":"
    // Player shooting\nif (fireInputReady && player->wantsToShoot()) {\n    // Check if player bullet already active\n    bool hasPlayerBullet = false;\n    for (auto* proj : projectiles) {\n        if (proj->isActive() && proj->getType() == ProjectileType::PLAYER_BULLET) {\n            hasPlayerBullet = true;\n            break;\n        }\n    }\n\n    if (!hasPlayerBullet) {\n        // Find inactive projectile and reuse it\n        for (auto* proj : projectiles) {\n            if (!proj->isActive()) {\n                float px = player->x + (PLAYER_WIDTH - PROJECTILE_WIDTH) / 2.0f;\n                float py = player->y - PROJECTILE_HEIGHT;\n                proj->reset(px, py, ProjectileType::PLAYER_BULLET);\n\n                // Play shoot sound\n                AudioEvent event{};\n                event.type = WaveType::PULSE;\n                event.frequency = 880.0f;\n                event.duration = 0.08f;\n                event.volume = 0.4f;\n                engine.getAudioEngine().playEvent(event);\n                break;\n            }\n        }\n    }\n}\n
    "},{"location":"examples/space_invaders/overview/#collision-detection-with-sweep-test","title":"Collision Detection with Sweep Test","text":"
    // Check projectile vs alien collision\nCircle startCircle;\nstartCircle.x = proj->getPreviousX() + radius;\nstartCircle.y = proj->getPreviousY() + PROJECTILE_HEIGHT * 0.5f;\nstartCircle.radius = radius;\n\nCircle endCircle;\nendCircle.x = proj->x + radius;\nendCircle.y = proj->y + PROJECTILE_HEIGHT * 0.5f;\nendCircle.radius = radius;\n\nfloat tHit = 0.0f;\npixelroot32::core::Rect targetBox = alien->getHitBox();\n\nif (sweepCircleVsRect(startCircle, endCircle, targetBox, tHit) ||\n    proj->getHitBox().intersects(targetBox)) {\n    // Hit! Deactivate projectile and kill alien\n    proj->deactivate();\n    alien->kill();\n    score += alien->getScoreValue();\n    spawnEnemyExplosion(alien->x + alien->width * 0.5f, \n                        alien->y + alien->height * 0.5f);\n}\n
    "},{"location":"examples/space_invaders/overview/#running-the-example","title":"Running the Example","text":""},{"location":"examples/space_invaders/overview/#prerequisites","title":"Prerequisites","text":"
    • PixelRoot32 Game Engine installed
    • ESP32 or Native build configured
    • Display and input configured
    "},{"location":"examples/space_invaders/overview/#build-and-run","title":"Build and Run","text":"
    1. Include the example in your project:

      #include \"examples/SpaceInvaders/SpaceInvadersScene.h\"\n

    2. In your main.cpp:

      spaceinvaders::SpaceInvadersScene gameScene;\n\nvoid setup() {\n    engine.init();\n    gameScene.init();\n    engine.setScene(&gameScene);\n}\n

    3. Build and upload (ESP32) or run (Native)

    "},{"location":"examples/space_invaders/overview/#controls","title":"Controls","text":"
    • LEFT/RIGHT: Move player ship
    • FIRE: Shoot projectile
    • FIRE (on game over): Restart game
    "},{"location":"examples/space_invaders/overview/#see-also","title":"See Also","text":"
    • Game Examples Guide - Analysis of all game examples
    • Manual - Scenes and Entities - Scene system
    • Manual - Physics and Collisions - Collision detection
    • Manual - Audio - Audio system
    • Manual - Sprites and Animation - Sprite system
    • Manual - Memory Management - Object pooling
    "},{"location":"getting_started/fundamental_concepts/","title":"Fundamental Concepts","text":"

    Before you start programming, it's important to understand the basic concepts that form PixelRoot32's architecture. This section explains how the engine works at a conceptual level, without going into code details.

    "},{"location":"getting_started/fundamental_concepts/#engine-architecture","title":"Engine Architecture","text":""},{"location":"getting_started/fundamental_concepts/#engine-the-heart-of-the-engine","title":"Engine: The Heart of the Engine","text":"

    The Engine is the main class that orchestrates the entire system. Think of it as the conductor that coordinates all subsystems:

    • Renderer: Handles drawing everything on screen
    • InputManager: Reads and processes user input (buttons, keyboard)
    • AudioEngine: Generates and plays sounds and music
    • SceneManager: Manages game scenes (menus, levels, etc.)

    The Engine runs the main game loop: an infinite cycle that updates game logic and draws each frame on screen. It also calculates delta time (time elapsed between frames) so the game runs at the same speed regardless of framerate.

    "},{"location":"getting_started/fundamental_concepts/#scene-organizing-your-game","title":"Scene: Organizing Your Game","text":"

    A Scene represents a screen or level in your game. For example: - A scene for the main menu - A scene for each game level - A scene for the game over screen - A scene for the pause menu

    Each scene contains and manages a set of entities (characters, enemies, objects, etc.). The scene is responsible for: - Initializing its entities when loaded - Updating the logic of all its entities each frame - Drawing all its visible entities each frame - Managing collisions between entities that can collide

    The Engine can only have one active scene at a time, but you can easily switch between scenes (for example, go from menu to game, or from game to pause menu).

    "},{"location":"getting_started/fundamental_concepts/#entity-the-fundamental-building-blocks","title":"Entity: The Fundamental Building Blocks","text":"

    An Entity is any object in your game that has: - Position (x, y) in the world - Size (width and height) - Visibility (can be visible or not) - Active state (can be enabled or disabled) - Render layer (in what order it's drawn)

    Entities are the foundation of everything in your game: the player, enemies, projectiles, objects, UI elements\u2014everything is an entity or inherits from Entity.

    Each entity has two main methods: - update(): Called each frame to update the entity's logic (movement, animation, etc.) - draw(): Called each frame to draw the entity on screen

    "},{"location":"getting_started/fundamental_concepts/#actor-entities-that-can-collide","title":"Actor: Entities That Can Collide","text":"

    An Actor is a special entity that can participate in the collision system. In addition to everything an Entity has, an Actor has: - Collision layer: Which group it belongs to (e.g., \"player\", \"enemy\", \"projectile\") - Collision mask: Which other groups it can collide with - Hitbox: The shape used to detect collisions (usually a rectangle)

    For example, a player might be on the \"player\" layer and have a mask that allows it to collide with \"enemies\" and \"obstacles\", but not with \"other players\".

    When two actors collide, the system calls their onCollision() method so they can react (e.g., player loses health, enemy is destroyed, etc.).

    "},{"location":"getting_started/fundamental_concepts/#physicsactor-entities-with-physics","title":"PhysicsActor: Entities with Physics","text":"

    A PhysicsActor is an Actor that also has physical properties: - Velocity (vx, vy): Moves automatically according to its velocity - Gravity: Can fall automatically - Friction: Gradually loses velocity - Restitution: Bounces when it collides (like a ball)

    The PhysicsActor updates automatically each frame, applying physics and moving the entity. It can also detect collisions with world boundaries (the walls of the play area).

    "},{"location":"getting_started/fundamental_concepts/#entity-hierarchy","title":"Entity Hierarchy","text":"

    The relationship between these classes is hierarchical:

    Entity (base)\n  \u2514\u2500\u2500 Actor (can collide)\n       \u2514\u2500\u2500 PhysicsActor (has physics)\n

    This means: - Every Actor is also an Entity - Every PhysicsActor is also an Actor and an Entity - You can use Entity for simple objects that don't need collisions - You can use Actor for objects that need to detect collisions - You can use PhysicsActor for objects that need automatic physics

    "},{"location":"getting_started/fundamental_concepts/#rendering-system","title":"Rendering System","text":""},{"location":"getting_started/fundamental_concepts/#render-layers","title":"Render Layers","text":"

    To control the order in which things are drawn, PixelRoot32 uses render layers:

    • Layer 0 (Background): Backgrounds, tilemaps, background elements
    • Layer 1 (Gameplay): Characters, enemies, projectiles, game objects
    • Layer 2 (UI): Menus, HUD, text, interface elements

    Layers are drawn in order: first 0, then 1, and finally 2. This ensures the background is always behind, gameplay in the middle, and UI always visible in front.

    Each entity has a renderLayer property that indicates which layer it should be drawn on. You can change this property to move entities between layers.

    "},{"location":"getting_started/fundamental_concepts/#rendering-pipeline","title":"Rendering Pipeline","text":"

    The rendering process works like this:

    1. beginFrame(): The screen is cleared (painted black or background color)
    2. Draw entities: All visible entities are traversed, organized by layer
    3. endFrame(): The complete frame is sent to the display

    The Renderer abstracts hardware details, so the same code works on both ESP32 (TFT_eSPI) and PC (SDL2).

    "},{"location":"getting_started/fundamental_concepts/#coordinates-and-space","title":"Coordinates and Space","text":"

    PixelRoot32 uses a standard coordinate system: - Origin (0, 0): Top-left corner - X-axis: Increases to the right - Y-axis: Increases downward

    Coordinates are in pixels. If your display is 240x240, coordinates range from (0, 0) to (239, 239).

    "},{"location":"getting_started/fundamental_concepts/#lifecycle","title":"Lifecycle","text":""},{"location":"getting_started/fundamental_concepts/#initialization","title":"Initialization","text":"

    When your game starts:

    1. Configuration: Configuration objects are created (DisplayConfig, InputConfig, AudioConfig)
    2. Engine: The Engine is created with these configurations
    3. init(): engine.init() is called to initialize all subsystems
    4. Scene: The initial scene is created and configured
    5. setScene(): The scene is assigned to the Engine
    "},{"location":"getting_started/fundamental_concepts/#game-loop","title":"Game Loop","text":"

    Once initialized, the Engine enters the game loop:

    While the game is running:\n  1. Calculate deltaTime (time since last frame)\n  2. Update InputManager (read buttons/keyboard)\n  3. Update AudioEngine (advance sounds and music)\n  4. Update current scene (update all entities)\n  5. Detect collisions in the scene\n  6. Draw the scene (draw all visible entities)\n  7. Repeat\n

    This cycle runs continuously, typically at 30-60 FPS on ESP32, or faster on PC.

    "},{"location":"getting_started/fundamental_concepts/#update","title":"Update","text":"

    Each frame, all enabled entities receive a call to their update(deltaTime) method. This is where: - Entities move - Animations update - Game logic is processed - User input is read - Sound effects are played

    The deltaTime is passed in milliseconds and represents how much time has passed since the last frame. This allows movement to be framerate-independent.

    "},{"location":"getting_started/fundamental_concepts/#rendering-draw","title":"Rendering (Draw)","text":"

    After updating, all visible entities receive a call to their draw(renderer) method. This is where: - Sprites are drawn - Text is drawn - Primitives are drawn (rectangles, circles, etc.)

    The renderer is passed as a parameter so entities can draw themselves.

    "},{"location":"getting_started/fundamental_concepts/#cleanup","title":"Cleanup","text":"

    When you change scenes or end the game: - Entities from the previous scene can be cleaned up - Resources are freed - The new scene is initialized

    "},{"location":"getting_started/fundamental_concepts/#conceptual-summary","title":"Conceptual Summary","text":"

    To summarize, PixelRoot32 works like this:

    1. Engine coordinates everything and runs the game loop
    2. Scene organizes your game into screens/levels
    3. Entity is any object in your game
    4. Actor is an entity that can collide
    5. PhysicsActor is an actor with automatic physics
    6. Renderer draws everything on screen using layers
    7. Each frame updates logic and then draws

    All of this works automatically once you configure the Engine and create your scenes and entities. You don't need to worry about game loop details; you just need to implement update() and draw() in your entities.

    "},{"location":"getting_started/fundamental_concepts/#next-step","title":"Next Step","text":"

    Now that you understand the fundamental concepts, you're ready to create your first project and see these concepts in action with real code.

    See also: - What is PixelRoot32? - Why PixelRoot32? - Your First Project - Manual - Scenes and Entities

    "},{"location":"getting_started/installation/","title":"Installation","text":"

    This guide covers installing the PixelRoot32 documentation environment and preparing your development setup for ESP32 and Native (PC) targets.

    "},{"location":"getting_started/installation/#requirements","title":"Requirements","text":"
    • Python 3.11 or newer
    • Git (recommended for source management)
    • VS Code (or your preferred IDE)
    • For ESP32 targets: PlatformIO (VS Code extension) with ESP32 toolchain
    • For Native targets: a C++ build toolchain (CMake or your OS-native toolchain)
    "},{"location":"getting_started/installation/#install-documentation-tooling","title":"Install Documentation Tooling","text":"

    To build and preview this documentation locally:

    pip install mkdocs mkdocs-material mkdocs-minify-plugin mkdocs-git-revision-date-localized-plugin mike\nmkdocs serve\n

    Open http://127.0.0.1:8000 in your browser to preview.

    "},{"location":"getting_started/installation/#esp32-setup-recommended","title":"ESP32 Setup (Recommended)","text":"
    1. Install VS Code
    2. Install PlatformIO IDE extension
    3. Install ESP32 platform/toolchain via PlatformIO
    4. Clone the engine repository:
    5. https://github.com/Gperez88/PixelRoot32-Game-Engine
    6. Open the engine or example project in VS Code (PlatformIO)
    7. Build and upload to your ESP32 board

    Tip: Use boards based on ESP32-WROOM/WROVER for best compatibility. Ensure a reliable USB cable and correct serial port selection.

    "},{"location":"getting_started/installation/#native-pc-setup","title":"Native (PC) Setup","text":"
    1. Install a C++ toolchain (e.g., MSVC or MinGW on Windows)
    2. Install CMake (if the engine provides CMake build files)
    3. Clone the engine repository:
    4. https://github.com/Gperez88/PixelRoot32-Game-Engine
    5. Configure and build the native runtime:
    6. Follow the engine\u2019s native build instructions (Development \u2192 Compiling)
    "},{"location":"getting_started/installation/#verify-your-environment","title":"Verify Your Environment","text":"
    • ESP32: Build and flash a minimal sample; confirm serial output and display if applicable
    • Native: Run the executable; confirm window output and input handling
    "},{"location":"getting_started/installation/#troubleshooting","title":"Troubleshooting","text":"
    • If PlatformIO cannot find the ESP32 platform, update PlatformIO and retry
    • If native builds fail, verify compiler versions and CMake generator settings
    • Use Community \u2192 Troubleshooting for common issues and fixes
    "},{"location":"getting_started/installation/#next-steps","title":"Next Steps","text":"
    • First Project
    • Concepts
    "},{"location":"getting_started/what_is_pixelroot32/","title":"What is PixelRoot32?","text":"

    PixelRoot32 is a lightweight, modular 2D game engine written in C++ designed specifically for ESP32 microcontrollers, with a native simulation layer for PC (SDL2) that allows you to develop and debug quickly on your desktop before deploying to hardware.

    "},{"location":"getting_started/what_is_pixelroot32/#simple-definition","title":"Simple Definition","text":"

    PixelRoot32 is a game engine that lets you create retro-style 8-bit/16-bit video games directly on an ESP32 board, with the ability to develop and test on your PC before transferring code to hardware.

    "},{"location":"getting_started/what_is_pixelroot32/#key-features","title":"Key Features","text":""},{"location":"getting_started/what_is_pixelroot32/#scene-based-architecture","title":"\ud83c\udfae Scene-Based Architecture","text":"
    • Scene system inspired by Godot Engine
    • Intuitive management of levels, menus, and screens
    • Simple transitions between scenes
    "},{"location":"getting_started/what_is_pixelroot32/#optimized-rendering","title":"\ud83c\udfa8 Optimized Rendering","text":"
    • 1bpp (monochrome) sprites as the standard format
    • Support for multi-layer sprites (MultiSprite)
    • Experimental 2bpp and 4bpp formats for higher fidelity
    • Retro color palette system (NES, GameBoy, PICO-8, etc.)
    • Compact tilemaps for backgrounds and levels
    • 2D camera with dead-zone for smooth scrolling
    • Render layer system (background, gameplay, UI)
    "},{"location":"getting_started/what_is_pixelroot32/#nes-like-audio","title":"\ud83d\udd0a NES-like Audio","text":"
    • 4 audio channels (2 Pulse, 1 Triangle, 1 Noise)
    • Integrated sound effects system
    • Music player for background melodies
    • Backends for ESP32 (internal DAC or external I2S) and SDL2
    "},{"location":"getting_started/what_is_pixelroot32/#physics-and-collisions","title":"\ud83c\udfaf Physics and Collisions","text":"
    • AABB (Axis-Aligned Bounding Box) collision system
    • PhysicsActor with gravity, friction, and restitution
    • Collision layers and masks for fine control
    • World boundary collision detection
    "},{"location":"getting_started/what_is_pixelroot32/#user-interface","title":"\ud83d\udda5\ufe0f User Interface","text":"
    • Basic elements: Labels, Buttons, Panels
    • Automatic layouts: Vertical, Horizontal, Grid, Anchor
    • Integrated D-pad navigation
    • Scroll and viewport culling for long lists
    "},{"location":"getting_started/what_is_pixelroot32/#optimized-for-esp32","title":"\u26a1 Optimized for ESP32","text":"
    • Efficient memory management
    • Integrated object pooling
    • No dynamic allocations in the game loop
    • Performance optimized for limited hardware
    "},{"location":"getting_started/what_is_pixelroot32/#typical-use-cases","title":"Typical Use Cases","text":"

    PixelRoot32 is ideal for creating:

    • Arcade Games: Space Invaders, Pong, Breakout
    • Platformers: Horizontal scrolling games with simple physics
    • Puzzles: Tetris, Snake, logic games
    • Simple RPGs: Basic role-playing games with tilemaps
    • Shooters: Vertical or horizontal shooting games
    • Rapid Prototypes: Quick development of game ideas
    "},{"location":"getting_started/what_is_pixelroot32/#supported-platforms","title":"Supported Platforms","text":""},{"location":"getting_started/what_is_pixelroot32/#esp32","title":"ESP32","text":"
    • Display: TFT_eSPI (ST7735, ILI9341, ST7789, etc.)
    • Audio: Internal DAC (GPIO 25/26) or external I2S (MAX98357A, PCM5102)
    • Input: Digital buttons, D-pad
    • Hardware: Any ESP32 board (ESP32-WROOM, ESP32-WROVER, etc.)
    "},{"location":"getting_started/what_is_pixelroot32/#desktopnative-pc","title":"Desktop/Native (PC)","text":"
    • Display: SDL2 (Windows, Linux, macOS)
    • Audio: SDL2 Audio
    • Input: Keyboard, mouse
    • Usage: Development, debugging, testing

    Note: Support for u8g2 (OLEDs) is planned for the future.

    "},{"location":"getting_started/what_is_pixelroot32/#project-status","title":"Project Status","text":"

    Current Version: v0.2.0-dev

    PixelRoot32 is under active development. APIs may change and some subsystems are still experimental. Occasional changes or breaking changes are expected, especially on less-tested configurations.

    "},{"location":"getting_started/what_is_pixelroot32/#stable-features","title":"Stable Features","text":"
    • Scene and entity system
    • Basic rendering (1bpp sprites)
    • NES-like audio system
    • Basic physics and collisions
    • Basic UI system
    • ESP32 and Native support
    "},{"location":"getting_started/what_is_pixelroot32/#experimental-features","title":"Experimental Features","text":"
    • 2bpp and 4bpp sprites (require compilation flags)
    • Scene Arena (advanced memory management)
    "},{"location":"getting_started/what_is_pixelroot32/#planned-features","title":"Planned Features","text":"
    • Support for u8g2 (OLEDs)
    • Music compiler
    • Tilemap compiler
    • Save/load system
    • Spatial partitioning for collisions
    "},{"location":"getting_started/what_is_pixelroot32/#quick-comparison","title":"Quick Comparison","text":""},{"location":"getting_started/what_is_pixelroot32/#when-to-use-pixelroot32","title":"When to use PixelRoot32?","text":"

    \u2705 Use PixelRoot32 if: - You want to create retro games on ESP32 - You need a lightweight and efficient engine - You prefer a simple and clear architecture - You want to develop on PC and deploy to ESP32 - You like 8-bit/16-bit style games

    \u274c Don't use PixelRoot32 if: - You need 3D graphics - You require advanced shaders - You need complex physics (advanced physics engines) - You want to create modern AAA games - You need support for multiple mobile platforms

    "},{"location":"getting_started/what_is_pixelroot32/#next-step","title":"Next Step","text":"

    Now that you understand what PixelRoot32 is, discover why you should use it or go directly to your first project.

    See also: - Fundamental Concepts - Installation - API Reference

    "},{"location":"getting_started/why_pixelroot32/","title":"Why PixelRoot32?","text":"

    PixelRoot32 is specifically designed to solve the unique challenges of creating video games on embedded hardware like the ESP32, while maintaining the simplicity and productivity of modern development.

    "},{"location":"getting_started/why_pixelroot32/#main-advantages","title":"Main Advantages","text":""},{"location":"getting_started/why_pixelroot32/#optimized-for-esp32","title":"\ud83c\udfaf Optimized for ESP32","text":"

    Memory Efficient - 1bpp sprite system that minimizes RAM and Flash usage - Integrated object pooling to avoid memory fragmentation - Compact tilemaps that reuse sprites - No dynamic allocations in the game loop

    Performance Optimized - Rendering optimized for ESP32 limitations - Efficient render layer system - Viewport culling to reduce draw calls - Rendering pipeline designed for limited hardware

    Real Hardware - Direct support for common TFT displays (ST7735, ILI9341, ST7789) - Integrated audio (internal DAC or external I2S) - Simple pin and hardware configuration

    "},{"location":"getting_started/why_pixelroot32/#cross-platform-development","title":"\ud83d\udda5\ufe0f Cross-Platform Development","text":"

    Develop on PC, Deploy to ESP32 - Same code works on PC (SDL2) and ESP32 - Fast debugging on desktop - Testing without hardware needed - Rapid development iteration

    Visual Consistency - Native bitmap font system (pixel-perfect) - Same rendering on PC and ESP32 - Consistent color palettes - No surprises when transferring to hardware

    "},{"location":"getting_started/why_pixelroot32/#retro-palette-system","title":"\ud83c\udfa8 Retro Palette System","text":"

    Authentic Style - Predefined palettes: NES, GameBoy, GameBoy Color, PICO-8 - Dual palette mode for visual contrasts - Custom palettes for unique styles - Automatic color resolution (RGB565)

    Easy to Use - Change palette with one line of code - Consistent visualization across all sprites - No need to manually convert assets

    "},{"location":"getting_started/why_pixelroot32/#integrated-audio","title":"\ud83d\udd0a Integrated Audio","text":"

    Complete NES-like System - 4 audio channels (2 Pulse, 1 Triangle, 1 Noise) - Simple sound effects to create - Integrated music system - Backends for different hardware configurations

    No External Dependencies - Software-generated audio - No heavy audio libraries required - Full control over sound - Deterministic and predictable

    "},{"location":"getting_started/why_pixelroot32/#simple-and-clear-architecture","title":"\ud83c\udfd7\ufe0f Simple and Clear Architecture","text":"

    Easy to Understand - Intuitive scene system (inspired by Godot) - Clear hierarchy: Entity \u2192 Actor \u2192 PhysicsActor - Consistent and predictable APIs - Clean and well-organized code

    Quick to Learn - Familiar concepts for game developers - Clear documentation and complete examples - Smooth learning curve - Active community and support

    "},{"location":"getting_started/why_pixelroot32/#complete-features","title":"\ud83c\udfae Complete Features","text":"

    Everything Needed for Games - Rendering (sprites, tilemaps, primitives) - Audio (effects and music) - Physics (gravity, collisions, basic physics) - UI (layouts, buttons, navigation) - Input (buttons, keyboard) - Camera (scroll, parallax)

    No Bloat - Only the essentials, nothing more - No heavy dependencies - Small and maintainable codebase - Easy to understand and modify

    "},{"location":"getting_started/why_pixelroot32/#tools-and-ecosystem","title":"\ud83d\udee0\ufe0f Tools and Ecosystem","text":"

    Available Tools - Sprite Compiler to convert PNG to sprites - Complete game examples - Templates and starter code - Extensive documentation

    Community and Support - Active and developing project - Open source (MIT License) - Feedback and contributions welcome - Examples available

    "},{"location":"getting_started/why_pixelroot32/#comparison-with-alternatives","title":"Comparison with Alternatives","text":""},{"location":"getting_started/why_pixelroot32/#vs-full-engines-unity-godot-etc","title":"vs. Full Engines (Unity, Godot, etc.)","text":"

    PixelRoot32 Advantages: - \u2705 Much lighter (fits in ESP32) - \u2705 No unnecessary overhead - \u2705 Full control over code - \u2705 Specifically optimized for limited hardware

    Disadvantages: - \u274c Fewer advanced features - \u274c No visual editor - \u274c Fewer resources and community

    "},{"location":"getting_started/why_pixelroot32/#vs-writing-everything-from-scratch","title":"vs. Writing Everything from Scratch","text":"

    PixelRoot32 Advantages: - \u2705 Rendering system already implemented - \u2705 Integrated and working audio - \u2705 Physics and collisions ready - \u2705 Complete UI system - \u2705 Saves months of development

    Disadvantages: - \u274c Less control over internal implementation - \u274c You must learn the engine API

    "},{"location":"getting_started/why_pixelroot32/#vs-other-esp32-engines","title":"vs. Other ESP32 Engines","text":"

    PixelRoot32 Advantages: - \u2705 More modern and clear architecture - \u2705 Better documentation - \u2705 Unique palette system - \u2705 Integrated NES-like audio - \u2705 Real cross-platform development

    "},{"location":"getting_started/why_pixelroot32/#ideal-use-cases","title":"Ideal Use Cases","text":"

    PixelRoot32 is perfect for:

    1. Educational Projects
    2. Learn game development
    3. Understand engine architecture
    4. Student projects

    5. Rapid Prototypes

    6. Quickly validate game ideas
    7. Create demos and proof-of-concepts
    8. Test mechanics

    9. Retro Games

    10. 8-bit/16-bit style games
    11. Arcade games
    12. Games with retro aesthetics

    13. Hardware Projects

    14. Games on small displays
    15. DIY portable consoles
    16. Maker/retro projects

    17. C++ Learning

    18. Clean and well-structured code
    19. Good programming practices
    20. Real and functional examples
    "},{"location":"getting_started/why_pixelroot32/#limitations-to-consider","title":"Limitations to Consider","text":"

    To be honest, PixelRoot32 has limitations:

    • Limited Hardware: Designed for ESP32, not powerful PCs
    • Simple Graphics: No 3D, no advanced shaders
    • Basic Physics: Not a complete physics engine
    • Restricted Memory: MAX_ENTITIES = 32 per scene
    • In Development: Some features are experimental

    If you need advanced features or powerful hardware, consider other engines. But for retro games on ESP32, PixelRoot32 is an excellent choice.

    "},{"location":"getting_started/why_pixelroot32/#conclusion","title":"Conclusion","text":"

    PixelRoot32 combines:

    • \u2705 Simplicity of use
    • \u2705 Efficiency for limited hardware
    • \u2705 Completeness of essential features
    • \u2705 Clarity of architecture
    • \u2705 Productivity in development

    If you want to create retro games on ESP32 without the complexity of large engines, PixelRoot32 is the right choice.

    "},{"location":"getting_started/why_pixelroot32/#next-step","title":"Next Step","text":"

    Now that you understand why PixelRoot32 is a good option, learn the fundamental concepts or start directly with your first project.

    See also: - What is PixelRoot32? - Fundamental Concepts - Your First Project

    "},{"location":"getting_started/your_first_project/","title":"Your First Project","text":"

    This guide will walk you through creating and running your first PixelRoot32 project step by step. By the end, you'll have a working project that displays a simple scene on both ESP32 and PC.

    "},{"location":"getting_started/your_first_project/#prerequisites","title":"Prerequisites","text":""},{"location":"getting_started/your_first_project/#required-software","title":"Required Software","text":"
    • PlatformIO: Install the PlatformIO IDE extension in VS Code
    • Open VS Code
    • Go to Extensions (Ctrl+Shift+X)
    • Search for \"PlatformIO IDE\"
    • Install and restart VS Code

    • Python 3.8+: Required for PlatformIO (usually installed automatically)

    "},{"location":"getting_started/your_first_project/#for-esp32-development","title":"For ESP32 Development","text":"
    • ESP32 Board: Any ESP32 development board (ESP32-WROOM, ESP32-WROVER, etc.)
    • USB Cable: To connect and program your ESP32
    • TFT Display: Compatible display (ST7735, ST7789, ILI9341, etc.)
    • Buttons: 5-6 digital buttons for input (optional for first project)
    • Audio Hardware (optional): Speaker + amplifier (PAM8302A) or I2S DAC (MAX98357A)
    "},{"location":"getting_started/your_first_project/#for-native-pc-development","title":"For Native (PC) Development","text":"
    • SDL2: Development libraries
    • Windows (MSYS2): pacman -S mingw-w64-x86_64-SDL2
    • Linux: sudo apt-get install libsdl2-dev
    • macOS: brew install sdl2
    "},{"location":"getting_started/your_first_project/#step-1-create-a-new-platformio-project","title":"Step 1: Create a New PlatformIO Project","text":"
    1. Open VS Code with PlatformIO installed

    2. Create New Project:

    3. Click on the PlatformIO icon in the sidebar
    4. Click \"New Project\"
    5. Name: my-first-pixelroot32-game
    6. Board: Select \"ESP32 Dev Module\" (or your specific board)
    7. Framework: Arduino
    8. Location: Choose your workspace folder
    9. Click \"Finish\"

    10. Project Structure: Your project should now have this structure:

      my-first-pixelroot32-game/\n\u251c\u2500\u2500 .pio/\n\u251c\u2500\u2500 include/\n\u251c\u2500\u2500 lib/\n\u251c\u2500\u2500 src/\n\u2502   \u2514\u2500\u2500 main.cpp\n\u251c\u2500\u2500 test/\n\u2514\u2500\u2500 platformio.ini\n

    "},{"location":"getting_started/your_first_project/#step-2-install-pixelroot32-engine","title":"Step 2: Install PixelRoot32 Engine","text":""},{"location":"getting_started/your_first_project/#option-a-via-platformio-library-manager-recommended","title":"Option A: Via PlatformIO Library Manager (Recommended)","text":"
    1. Open platformio.ini

    2. Add the library dependency:

    [env:esp32dev]\nplatform = espressif32\nboard = esp32dev\nframework = arduino\nlib_deps = \n    gperez88/PixelRoot32-Game-Engine@0.2.0-dev\n

    \u26a0\ufe0f IMPORTANT: Use the exact version 0.2.0-dev. Do NOT use ^ or fuzzy versioning.

    1. Save the file. PlatformIO will automatically download the library.
    "},{"location":"getting_started/your_first_project/#option-b-git-submodule","title":"Option B: Git Submodule","text":"
    1. Open terminal in your project root

    2. Add as submodule:

      git submodule add https://github.com/Gperez88/PixelRoot32-Game-Engine.git lib/PixelRoot32-Game-Engine\n

    3. Update platformio.ini:

      lib_extra_dirs = lib\n

    "},{"location":"getting_started/your_first_project/#step-3-configure-hardware-esp32","title":"Step 3: Configure Hardware (ESP32)","text":""},{"location":"getting_started/your_first_project/#configure-tft_espi-display","title":"Configure TFT_eSPI Display","text":"

    Edit platformio.ini and add build flags for your display. Here are two common configurations:

    For ST7789 (240x240):

    [env:esp32dev]\nplatform = espressif32\nboard = esp32dev\nframework = arduino\nlib_deps = \n    gperez88/PixelRoot32-Game-Engine@0.2.0-dev\n    bodmer/TFT_eSPI@^2.5.43\n\nbuild_flags = \n    -D ST7789_DRIVER\n    -D TFT_WIDTH=240\n    -D TFT_HEIGHT=240\n    -D TFT_MOSI=23\n    -D TFT_SCLK=18\n    -D TFT_DC=2\n    -D TFT_RST=4\n    -D TFT_CS=-1\n    -D LOAD_GLCD\n    -D LOAD_FONT2\n    -D LOAD_FONT4\n    -D LOAD_FONT6\n    -D LOAD_FONT7\n    -D LOAD_FONT8\n    -D LOAD_GFXFF\n    -D SMOOTH_FONT\n    -D SPI_FREQUENCY=40000000\n    -D SPI_READ_FREQUENCY=20000000\n

    For ST7735 (128x128):

    build_flags = \n    -D ST7735_DRIVER\n    -D ST7735_GREENTAB3\n    -D TFT_WIDTH=128\n    -D TFT_HEIGHT=128\n    -D TFT_MOSI=23\n    -D TFT_SCLK=18\n    -D TFT_DC=2\n    -D TFT_RST=4\n    -D TFT_CS=-1\n    -D LOAD_GLCD\n    -D LOAD_FONT2\n    -D LOAD_FONT4\n    -D LOAD_FONT6\n    -D LOAD_FONT7\n    -D LOAD_FONT8\n    -D LOAD_GFXFF\n    -D SMOOTH_FONT\n    -D SPI_FREQUENCY=27000000\n    -D SPI_READ_FREQUENCY=20000000\n

    Note: Adjust the pin numbers (TFT_MOSI, TFT_SCLK, TFT_DC, TFT_RST) to match your hardware wiring.

    "},{"location":"getting_started/your_first_project/#configure-input-optional-for-first-project","title":"Configure Input (Optional for First Project)","text":"

    If you have buttons connected, note the GPIO pins. For now, we'll create a project that works without input.

    "},{"location":"getting_started/your_first_project/#configure-audio-optional-for-first-project","title":"Configure Audio (Optional for First Project)","text":"

    Audio is optional for the first project. We'll add it later.

    "},{"location":"getting_started/your_first_project/#step-4-create-your-first-scene","title":"Step 4: Create Your First Scene","text":"

    Create a new file src/MyFirstScene.h:

    #pragma once\n#include <core/Scene.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\nclass MyFirstScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Called when the scene is initialized\n        // Set up your scene here\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Called every frame\n        // Update game logic here\n        Scene::update(deltaTime); // Don't forget to call parent update!\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Called every frame to draw\n        // Draw your scene here\n\n        // Example: Draw a simple rectangle\n        renderer.drawFilledRectangle(50, 50, 100, 100, pixelroot32::graphics::Color::Blue);\n\n        // Example: Draw text\n        renderer.drawText(\"Hello PixelRoot32!\", 20, 20, pixelroot32::graphics::Color::White, 2);\n\n        // Don't forget to call parent draw to draw all entities!\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"getting_started/your_first_project/#step-5-create-main-file-esp32","title":"Step 5: Create Main File (ESP32)","text":"

    Replace the contents of src/main.cpp with:

    #include <Arduino.h>\n#include <core/Engine.h>\n#include <drivers/esp32/TFT_eSPI_Drawer.h>\n#include <drivers/esp32/ESP32_DAC_AudioBackend.h>\n#include \"MyFirstScene.h\"\n\nnamespace pr32 = pixelroot32;\n\n// Audio configuration (optional - can be omitted for first project)\nconst int DAC_PIN = 25; // GPIO 25 or 26\npr32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(DAC_PIN, 11025);\n\n// Display configuration\n// ST7789, rotation 0, 240x240 resolution\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::ST7789, \n    0,      // rotation\n    240,    // width\n    240     // height\n);\n\n// Input configuration (6 buttons: UP, DOWN, LEFT, RIGHT, A, B)\n// For now, we'll use dummy pins - you can change these later\npr32::input::InputConfig inputConfig(\n    6,      // button count\n    32,     // UP pin\n    27,     // DOWN pin\n    33,     // LEFT pin\n    14,     // RIGHT pin\n    13,     // A button pin\n    12      // B button pin\n);\n\n// Audio configuration\npr32::audio::AudioConfig audioConfig(&audioBackend, audioBackend.getSampleRate());\n\n// Create the engine\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\n// Create your scene\nMyFirstScene myScene;\n\nvoid setup() {\n    Serial.begin(115200);\n\n    // Initialize the engine\n    engine.init();\n\n    // Initialize and set the scene\n    myScene.init();\n    engine.setScene(&myScene);\n\n    Serial.println(\"PixelRoot32 initialized!\");\n}\n\nvoid loop() {\n    // Run the game loop\n    engine.run();\n}\n
    "},{"location":"getting_started/your_first_project/#step-6-create-native-version-optional","title":"Step 6: Create Native Version (Optional)","text":"

    If you want to test on PC first, create src/main_native.cpp:

    #define SDL_MAIN_HANDLED\n#include <SDL2/SDL.h>\n#include <core/Engine.h>\n#include <drivers/native/SDL2_Drawer.h>\n#include <drivers/native/SDL2_AudioBackend.h>\n#include \"MyFirstScene.h\"\n\nnamespace pr32 = pixelroot32;\n\n// Audio configuration\npr32::drivers::native::SDL2_AudioBackend audioBackend(22050, 1024);\n\n// Display configuration (NONE defaults to SDL2 on Native)\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::NONE,\n    0,      // rotation\n    240,    // width\n    240     // height\n);\n\n// Input configuration (SDL scancodes)\npr32::input::InputConfig inputConfig(\n    6,                      // button count\n    SDL_SCANCODE_UP,        // UP\n    SDL_SCANCODE_DOWN,      // DOWN\n    SDL_SCANCODE_LEFT,      // LEFT\n    SDL_SCANCODE_RIGHT,     // RIGHT\n    SDL_SCANCODE_SPACE,     // A button\n    SDL_SCANCODE_RETURN     // B button\n);\n\n// Audio configuration\npr32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n\n// Create the engine\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\n// Create your scene\nMyFirstScene myScene;\n\nint main(int argc, char* argv[]) {\n    (void)argc;\n    (void)argv;\n\n    // Initialize the engine\n    engine.init();\n\n    // Initialize and set the scene\n    myScene.init();\n    engine.setScene(&myScene);\n\n    // Run the game loop\n    engine.run();\n\n    return 0;\n}\n
    "},{"location":"getting_started/your_first_project/#configure-native-build","title":"Configure Native Build","text":"

    Add to platformio.ini:

    [env:native]\nplatform = native\nbuild_src_filter = \n    +<*>\n    -<main.cpp>\nlib_extra_dirs = lib\nbuild_flags = \n    -D PLATFORM_NATIVE\n    -Isrc\n    -Ilib/PixelRoot32-Game-Engine/include\n    -IC:/msys64/mingw64/include/SDL2    # Windows MSYS2 path - adjust for your system\n    -LC:/msys64/mingw64/lib             # Windows MSYS2 path - adjust for your system\n    -O2\n    -Wall\n    -Wextra\n    -std=c++17\n    -lSDL2\n    -mconsole\n

    Note: Adjust the SDL2 include and library paths for your system.

    "},{"location":"getting_started/your_first_project/#step-7-build-and-run","title":"Step 7: Build and Run","text":""},{"location":"getting_started/your_first_project/#for-esp32","title":"For ESP32","text":"
    1. Connect your ESP32 via USB
    2. Select the environment: Click on the PlatformIO icon \u2192 Select env:esp32dev
    3. Build: Click the checkmark icon (\u2713) or press Ctrl+Alt+B
    4. Upload: Click the arrow icon (\u2192) or press Ctrl+Alt+U
    5. Monitor: Click the plug icon to open serial monitor

    You should see \"PixelRoot32 initialized!\" in the serial monitor and your display should show a blue rectangle and text.

    "},{"location":"getting_started/your_first_project/#for-native-pc","title":"For Native (PC)","text":"
    1. Select the environment: Click on the PlatformIO icon \u2192 Select env:native
    2. Build and Run: Click the play icon (\u25b6) or press Ctrl+Alt+R

    A window should open showing your scene with a blue rectangle and \"Hello PixelRoot32!\" text.

    "},{"location":"getting_started/your_first_project/#step-8-verify-it-works","title":"Step 8: Verify It Works","text":"

    If everything is set up correctly, you should see:

    • ESP32: Display shows a blue rectangle at (50, 50) and white text \"Hello PixelRoot32!\" at (20, 20)
    • Native: Window shows the same content

    If you see this, congratulations! Your first PixelRoot32 project is working.

    "},{"location":"getting_started/your_first_project/#troubleshooting","title":"Troubleshooting","text":""},{"location":"getting_started/your_first_project/#esp32-issues","title":"ESP32 Issues","text":"

    Display is blank: - Check wiring connections - Verify pin numbers in platformio.ini match your hardware - Check SPI frequency (try lowering it) - Verify display type (ST7789 vs ST7735)

    Compilation errors: - Ensure library version is exactly 0.2.0-dev - Check that TFT_eSPI is installed - Verify all include paths are correct

    Upload fails: - Check USB cable connection - Try different USB port - Press BOOT button on ESP32 during upload - Check COM port in PlatformIO

    "},{"location":"getting_started/your_first_project/#native-issues","title":"Native Issues","text":"

    SDL2 not found: - Verify SDL2 is installed - Check include/library paths in platformio.ini - On Windows, ensure MSYS2 paths are correct

    Window doesn't open: - Check console for error messages - Verify SDL2 is properly linked - Try running from terminal to see errors

    "},{"location":"getting_started/your_first_project/#next-steps","title":"Next Steps","text":"

    Now that you have a working project, you can:

    1. Learn about Scenes and Entities: See how to create game objects
    2. Add Input: Make your scene respond to buttons
    3. Add Sprites: Draw custom graphics
    4. Add Audio: Play sounds and music

    Continue with the Development Guide to learn more.

    See also: - Fundamental Concepts - Installation - Manual - Scenes and Entities - API Reference

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/","title":"Cameras and Scrolling","text":"

    Camera2D allows you to create worlds larger than the screen by scrolling the view. This guide covers camera setup, following targets, boundaries, and parallax effects.

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#camera2d-basics","title":"Camera2D Basics","text":"

    A Camera2D defines what portion of your game world is visible on screen.

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#creating-a-camera","title":"Creating a Camera","text":"
    #include <graphics/Camera2D.h>\n\n// Create camera with viewport size\npixelroot32::graphics::Camera2D camera(240, 240); // Screen width, height\n\n// Set camera position\ncamera.setPosition(0, 0);\n\n// Apply camera to renderer (in draw method)\ncamera.apply(renderer);\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#how-it-works","title":"How It Works","text":"

    The camera translates world coordinates to screen coordinates: - Objects at world position (100, 50) with camera at (0, 0) appear at screen (100, 50) - Objects at world position (100, 50) with camera at (50, 0) appear at screen (50, 50) - The camera effectively \"moves\" the world relative to the screen

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#following-a-target","title":"Following a Target","text":"

    The most common use is following a player or other target.

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#basic-follow","title":"Basic Follow","text":"
    class GameScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    PlayerActor* player;\n\npublic:\n    void init() override {\n        int screenWidth = engine.getRenderer().getWidth();\n        int screenHeight = engine.getRenderer().getHeight();\n\n        // Create camera\n        camera = pixelroot32::graphics::Camera2D(screenWidth, screenHeight);\n\n        // Create player\n        player = new PlayerActor(500, 300); // World position\n        addEntity(player);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Make camera follow player\n        camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Apply camera before drawing\n        camera.apply(renderer);\n\n        // Now all drawing uses camera coordinates\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#dead-zone-smooth-following","title":"Dead Zone (Smooth Following)","text":"

    For smoother following, you can implement a dead zone where the camera doesn't move until the target leaves the zone:

    void update(unsigned long deltaTime) override {\n    Scene::update(deltaTime);\n\n    // Get screen center\n    int screenCenterX = engine.getRenderer().getWidth() / 2;\n    int screenCenterY = engine.getRenderer().getHeight() / 2;\n\n    // Calculate player position relative to screen center\n    float playerScreenX = player->x - camera.getX();\n    float playerScreenY = player->y - camera.getY();\n\n    // Dead zone size\n    const int DEAD_ZONE_X = 40;\n    const int DEAD_ZONE_Y = 40;\n\n    // Move camera if player leaves dead zone\n    if (playerScreenX < screenCenterX - DEAD_ZONE_X) {\n        camera.setPosition(player->x - (screenCenterX - DEAD_ZONE_X), camera.getY());\n    } else if (playerScreenX > screenCenterX + DEAD_ZONE_X) {\n        camera.setPosition(player->x - (screenCenterX + DEAD_ZONE_X), camera.getY());\n    }\n\n    if (playerScreenY < screenCenterY - DEAD_ZONE_Y) {\n        camera.setPosition(camera.getX(), player->y - (screenCenterY - DEAD_ZONE_Y));\n    } else if (playerScreenY > screenCenterY + DEAD_ZONE_Y) {\n        camera.setPosition(camera.getX(), player->y - (screenCenterY + DEAD_ZONE_Y));\n    }\n}\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#camera-boundaries","title":"Camera Boundaries","text":"

    Limit camera movement to keep it within your level bounds.

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#setting-boundaries","title":"Setting Boundaries","text":"
    void init() override {\n    // Create camera\n    camera = pixelroot32::graphics::Camera2D(240, 240);\n\n    // Set horizontal boundaries (level is 2000 pixels wide)\n    camera.setBounds(0, 2000 - 240); // minX, maxX\n\n    // Set vertical boundaries (level is 1000 pixels tall)\n    camera.setVerticalBounds(0, 1000 - 240); // minY, maxY\n}\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#example-side-scroller-with-boundaries","title":"Example: Side-Scroller with Boundaries","text":"
    class SideScrollerScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    PlayerActor* player;\n    static const int LEVEL_WIDTH = 2000;\n    static const int LEVEL_HEIGHT = 240;\n\npublic:\n    void init() override {\n        int screenWidth = engine.getRenderer().getWidth();\n        int screenHeight = engine.getRenderer().getHeight();\n\n        camera = pixelroot32::graphics::Camera2D(screenWidth, screenHeight);\n\n        // Set boundaries (camera can't go outside level)\n        camera.setBounds(0, LEVEL_WIDTH - screenWidth);\n        camera.setVerticalBounds(0, LEVEL_HEIGHT - screenHeight);\n\n        // Create player at start\n        player = new PlayerActor(100, 100);\n        addEntity(player);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Follow player horizontally\n        camera.followTarget(player->x, camera.getY());\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        camera.apply(renderer);\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#parallax-scrolling","title":"Parallax Scrolling","text":"

    Parallax creates depth by moving background layers at different speeds.

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#basic-parallax","title":"Basic Parallax","text":"
    class ParallaxBackground : public pixelroot32::core::Entity {\nprivate:\n    float parallaxSpeed; // 0.0 to 1.0 (1.0 = normal, 0.5 = half speed)\n    float baseX;\n\npublic:\n    ParallaxBackground(float speed)\n        : Entity(0, 0, 240, 240, pixelroot32::core::EntityType::GENERIC),\n          parallaxSpeed(speed), baseX(0) {\n        setRenderLayer(0);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Get camera position\n        auto& camera = getCamera(); // You'll need to pass camera reference\n\n        // Calculate parallax offset\n        baseX = camera.getX() * parallaxSpeed;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw background with parallax offset\n        renderer.drawTileMap(backgroundTileMap, \n            static_cast<int>(baseX), 0, \n            pixelroot32::graphics::Color::White);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#multiple-parallax-layers","title":"Multiple Parallax Layers","text":"
    class ParallaxScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n\n    // Parallax layers (farther = slower)\n    ParallaxLayer* farBackground;    // Speed: 0.2\n    ParallaxLayer* midBackground;      // Speed: 0.5\n    ParallaxLayer* nearBackground;     // Speed: 0.8\n    PlayerActor* player;               // Speed: 1.0 (normal)\n\npublic:\n    void init() override {\n        camera = pixelroot32::graphics::Camera2D(240, 240);\n\n        // Create parallax layers\n        farBackground = new ParallaxLayer(0.2f);  // Moves slowest\n        midBackground = new ParallaxLayer(0.5f);\n        nearBackground = new ParallaxLayer(0.8f);\n\n        addEntity(farBackground);\n        addEntity(midBackground);\n        addEntity(nearBackground);\n\n        player = new PlayerActor(100, 100);\n        addEntity(player);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Update parallax layers with camera position\n        farBackground->updateParallax(camera.getX());\n        midBackground->updateParallax(camera.getX());\n        nearBackground->updateParallax(camera.getX());\n\n        // Follow player\n        camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        camera.apply(renderer);\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#using-setdisplayoffset-for-parallax","title":"Using setDisplayOffset for Parallax","text":"

    For simpler parallax, you can use setDisplayOffset():

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Apply camera\n    camera.apply(renderer);\n\n    // Draw far background with offset (moves slower)\n    renderer.setDisplayOffset(\n        static_cast<int>(camera.getX() * 0.3f), \n        0\n    );\n    renderer.drawTileMap(farBackground, 0, 0, Color::White);\n\n    // Draw mid background\n    renderer.setDisplayOffset(\n        static_cast<int>(camera.getX() * 0.6f), \n        0\n    );\n    renderer.drawTileMap(midBackground, 0, 0, Color::White);\n\n    // Reset offset for normal drawing\n    renderer.setDisplayOffset(0, 0);\n\n    // Draw game objects (normal speed)\n    Scene::draw(renderer);\n}\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#complete-example-platformer-with-camera","title":"Complete Example: Platformer with Camera","text":"
    class PlatformerScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    PlayerActor* player;\n    static const int LEVEL_WIDTH = 3000;\n    static const int LEVEL_HEIGHT = 800;\n\npublic:\n    void init() override {\n        int screenWidth = engine.getRenderer().getWidth();\n        int screenHeight = engine.getRenderer().getHeight();\n\n        // Create camera\n        camera = pixelroot32::graphics::Camera2D(screenWidth, screenHeight);\n\n        // Set boundaries\n        camera.setBounds(0, LEVEL_WIDTH - screenWidth);\n        camera.setVerticalBounds(0, LEVEL_HEIGHT - screenHeight);\n\n        // Create player\n        player = new PlayerActor(100, 400);\n        addEntity(player);\n\n        // Create platforms, enemies, etc.\n        // ...\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Follow player with dead zone\n        int screenCenterX = engine.getRenderer().getWidth() / 2;\n        int screenCenterY = engine.getRenderer().getHeight() / 2;\n\n        float playerScreenX = player->x - camera.getX();\n        float playerScreenY = player->y - camera.getY();\n\n        const int DEAD_ZONE = 60;\n\n        // Horizontal follow\n        if (playerScreenX < screenCenterX - DEAD_ZONE) {\n            camera.setPosition(player->x - (screenCenterX - DEAD_ZONE), camera.getY());\n        } else if (playerScreenX > screenCenterX + DEAD_ZONE) {\n            camera.setPosition(player->x - (screenCenterX + DEAD_ZONE), camera.getY());\n        }\n\n        // Vertical follow (only when falling or jumping high)\n        if (playerScreenY < screenCenterY - DEAD_ZONE || \n            playerScreenY > screenCenterY + DEAD_ZONE) {\n            camera.setPosition(camera.getX(), player->y - screenCenterY);\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Apply camera\n        camera.apply(renderer);\n\n        // Draw background (parallax)\n        renderer.setDisplayOffset(\n            static_cast<int>(camera.getX() * 0.3f), \n            0\n        );\n        renderer.drawTileMap(backgroundTileMap, 0, 0, Color::DarkGray);\n        renderer.setDisplayOffset(0, 0);\n\n        // Draw game objects\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#best-practices","title":"Best Practices","text":""},{"location":"manual/advanced_graphics/cameras_and_scrolling/#camera-movement","title":"Camera Movement","text":"
    • Use dead zones: Prevents jittery camera movement
    • Smooth transitions: Consider lerping camera position for smoother movement
    • Set boundaries: Always limit camera to level bounds
    • Test on hardware: Camera performance may differ on ESP32
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#parallax","title":"Parallax","text":"
    • Layer speeds: Farther layers move slower (0.2-0.5), closer move faster (0.7-0.9)
    • Limit layers: Too many parallax layers can impact performance
    • Use tilemaps: Parallax works best with tilemaps
    • Test visually: Ensure parallax effect is noticeable but not distracting
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#performance","title":"Performance","text":"
    • Apply once: Call camera.apply() once per frame, at start of draw()
    • Cull off-screen: Don't draw entities outside camera view
    • Limit parallax layers: 2-3 layers is usually enough
    • Optimize tilemaps: Use efficient tilemap rendering
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/advanced_graphics/cameras_and_scrolling/#camera-helper-class","title":"Camera Helper Class","text":"
    class CameraController {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    float targetX, targetY;\n    float smoothSpeed = 0.1f;\n\npublic:\n    void followTarget(float x, float y) {\n        targetX = x;\n        targetY = y;\n    }\n\n    void update(unsigned long deltaTime) {\n        // Smooth camera movement\n        float currentX = camera.getX();\n        float currentY = camera.getY();\n\n        float newX = currentX + (targetX - currentX) * smoothSpeed;\n        float newY = currentY + (targetY - currentY) * smoothSpeed;\n\n        camera.setPosition(newX, newY);\n    }\n\n    void apply(pixelroot32::graphics::Renderer& renderer) {\n        camera.apply(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#viewport-culling","title":"Viewport Culling","text":"

    Only draw entities within camera view:

    bool isVisible(float x, float y, int width, int height) {\n    float cameraX = camera.getX();\n    float cameraY = camera.getY();\n    int screenWidth = engine.getRenderer().getWidth();\n    int screenHeight = engine.getRenderer().getHeight();\n\n    return !(x + width < cameraX || \n             x > cameraX + screenWidth ||\n             y + height < cameraY || \n             y > cameraY + screenHeight);\n}\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/advanced_graphics/cameras_and_scrolling/#camera-not-moving","title":"Camera Not Moving","text":"
    • Verify camera.apply() is called in draw()
    • Check followTarget() or setPosition() is called in update()
    • Ensure camera is created with correct viewport size
    • Check boundaries aren't preventing movement
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#objects-not-visible","title":"Objects Not Visible","text":"
    • Verify objects are within camera view
    • Check world coordinates vs screen coordinates
    • Ensure camera is applied before drawing
    • Verify render layers are correct
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#parallax-not-working","title":"Parallax Not Working","text":"
    • Check setDisplayOffset() is used correctly
    • Verify parallax speed values (0.0 to 1.0)
    • Ensure offset is reset after parallax layers
    • Test with different speed values
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#next-steps","title":"Next Steps","text":"

    Now that you understand cameras and scrolling, learn about: - Tilemaps - Build levels with tiles - Particles and Effects - Add visual effects - Performance Optimization - Optimize your game

    See also: - API Reference - Camera2D - Manual - Basic Rendering - Manual - Tilemaps

    "},{"location":"manual/advanced_graphics/color_palettes/","title":"Color Palettes","text":"

    PixelRoot32 uses a palette-based color system that allows you to easily change the visual style of your game. This guide covers built-in palettes, dual palette mode, and custom palettes.

    "},{"location":"manual/advanced_graphics/color_palettes/#built-in-palettes","title":"Built-in Palettes","text":"

    PixelRoot32 includes several predefined palettes inspired by classic gaming systems:

    "},{"location":"manual/advanced_graphics/color_palettes/#available-palettes","title":"Available Palettes","text":"
    #include <graphics/PaletteDefs.h>\n\nnamespace pixelroot32::graphics {\n\nenum class PaletteType {\n    PR32,    // PixelRoot32 default palette\n    NES,     // Nintendo Entertainment System\n    GB,      // GameBoy (4 shades of green)\n    GBC,     // GameBoy Color\n    PICO8    // PICO-8 palette\n};\n
    "},{"location":"manual/advanced_graphics/color_palettes/#using-built-in-palettes","title":"Using Built-in Palettes","text":"
    #include <graphics/PaletteDefs.h>\n\n// Set palette globally (legacy mode)\npixelroot32::graphics::setPalette(pixelroot32::graphics::PaletteType::NES);\n\n// All sprites will now use NES colors\nrenderer.drawSprite(MY_SPRITE, 100, 100, pixelroot32::graphics::Color::White);\n
    "},{"location":"manual/advanced_graphics/color_palettes/#palette-characteristics","title":"Palette Characteristics","text":"

    PR32 (Default) - Modern, balanced colors - Good contrast - Suitable for most games

    NES - Classic 8-bit console colors - Limited color range - Nostalgic feel

    GB (GameBoy) - 4 shades of green - Monochrome aesthetic - Classic handheld look

    GBC (GameBoy Color) - Expanded color range - More vibrant than GB - Classic portable console

    PICO8 - PICO-8 fantasy console palette - 16 carefully chosen colors - Popular for retro games

    "},{"location":"manual/advanced_graphics/color_palettes/#legacy-mode-single-global-palette","title":"Legacy Mode (Single Global Palette)","text":"

    In legacy mode, one palette is used for all sprites:

    void MyScene::init() override {\n    // Set global palette\n    pixelroot32::graphics::setPalette(pixelroot32::graphics::PaletteType::NES);\n\n    // All sprites use NES colors\n    // This is the simplest mode\n}\n

    When to use: - Simple games - Consistent visual style - Maximum compatibility

    "},{"location":"manual/advanced_graphics/color_palettes/#dual-palette-mode","title":"Dual Palette Mode","text":"

    Dual palette mode allows different palettes for background elements and sprites, creating visual contrast.

    "},{"location":"manual/advanced_graphics/color_palettes/#enabling-dual-palette-mode","title":"Enabling Dual Palette Mode","text":"
    #include <graphics/PaletteDefs.h>\n\nvoid MyScene::init() override {\n    // Enable dual palette mode\n    pixelroot32::graphics::enableDualPaletteMode();\n\n    // Set background palette\n    pixelroot32::graphics::setBackgroundPalette(\n        pixelroot32::graphics::PaletteType::GB\n    );\n\n    // Set sprite palette\n    pixelroot32::graphics::setSpritePalette(\n        pixelroot32::graphics::PaletteType::NES\n    );\n}\n
    "},{"location":"manual/advanced_graphics/color_palettes/#how-it-works","title":"How It Works","text":"
    • Background palette: Used for tilemaps, primitives, and background sprites
    • Sprite palette: Used for game objects, characters, and foreground sprites
    • Automatic context: The renderer automatically selects the correct palette based on what you're drawing
    "},{"location":"manual/advanced_graphics/color_palettes/#example-contrasting-styles","title":"Example: Contrasting Styles","text":"
    void MyScene::init() override {\n    pixelroot32::graphics::enableDualPaletteMode();\n\n    // Dark, muted background (GameBoy green)\n    pixelroot32::graphics::setBackgroundPalette(\n        pixelroot32::graphics::PaletteType::GB\n    );\n\n    // Bright, colorful sprites (NES)\n    pixelroot32::graphics::setSpritePalette(\n        pixelroot32::graphics::PaletteType::NES\n    );\n\n    // Background uses GB palette\n    renderer.drawTileMap(backgroundTileMap, 0, 0, \n        pixelroot32::graphics::Color::White);\n\n    // Sprites use NES palette\n    renderer.drawSprite(playerSprite, 100, 100, \n        pixelroot32::graphics::Color::White);\n}\n
    "},{"location":"manual/advanced_graphics/color_palettes/#when-to-use-dual-palette-mode","title":"When to Use Dual Palette Mode","text":"
    • Visual contrast: Make sprites stand out from background
    • Artistic style: Different palettes for different layers
    • Retro aesthetics: Classic console color separation
    • Performance: No performance impact, just visual variety
    "},{"location":"manual/advanced_graphics/color_palettes/#custom-palettes","title":"Custom Palettes","text":"

    Create your own color palettes for unique visual styles.

    "},{"location":"manual/advanced_graphics/color_palettes/#creating-a-custom-palette","title":"Creating a Custom Palette","text":"
    #include <graphics/PaletteDefs.h>\n#include <graphics/Color.h>\n\n// Define custom colors (RGB565 format)\nstatic const pixelroot32::graphics::Color CUSTOM_PALETTE[] = {\n    pixelroot32::graphics::Color::Black,      // 0: Transparent/background\n    pixelroot32::graphics::Color::DarkBlue,   // 1\n    pixelroot32::graphics::Color::Blue,       // 2\n    pixelroot32::graphics::Color::LightBlue, // 3\n    pixelroot32::graphics::Color::Cyan,      // 4\n    pixelroot32::graphics::Color::White,      // 5\n    // ... more colors\n};\n\n// Set custom palette\npixelroot32::graphics::setCustomPalette(\n    CUSTOM_PALETTE,\n    sizeof(CUSTOM_PALETTE) / sizeof(pixelroot32::graphics::Color)\n);\n
    "},{"location":"manual/advanced_graphics/color_palettes/#rgb565-color-format","title":"RGB565 Color Format","text":"

    Colors in PixelRoot32 use RGB565 format (16-bit):

    // RGB565: 5 bits red, 6 bits green, 5 bits blue\n// Format: RRRRR GGGGGG BBBBB\n\n// Create custom RGB565 color\nuint16_t myColor = (31 << 11) | (63 << 5) | 31; // White\nuint16_t myColor = (0 << 11) | (0 << 5) | 0;    // Black\nuint16_t myColor = (31 << 11) | (0 << 5) | 0;   // Red\n\n// Or use Color constants\npixelroot32::graphics::Color::Red\npixelroot32::graphics::Color::Green\npixelroot32::graphics::Color::Blue\n
    "},{"location":"manual/advanced_graphics/color_palettes/#helper-function-for-custom-colors","title":"Helper Function for Custom Colors","text":"
    // Create RGB565 color from RGB values (0-255)\nuint16_t rgb565(uint8_t r, uint8_t g, uint8_t b) {\n    return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);\n}\n\n// Usage\nstatic const pixelroot32::graphics::Color MY_PALETTE[] = {\n    rgb565(0, 0, 0),        // Black\n    rgb565(255, 0, 0),      // Red\n    rgb565(0, 255, 0),      // Green\n    rgb565(0, 0, 255),      // Blue\n    rgb565(255, 255, 255),  // White\n};\n
    "},{"location":"manual/advanced_graphics/color_palettes/#complete-custom-palette-example","title":"Complete Custom Palette Example","text":"
    class CustomPaletteScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Define custom palette (ocean theme)\n        static const pixelroot32::graphics::Color OCEAN_PALETTE[] = {\n            pixelroot32::graphics::Color::Black,      // 0: Deep ocean\n            pixelroot32::graphics::Color::Navy,        // 1: Dark blue\n            pixelroot32::graphics::Color::Blue,       // 2: Medium blue\n            pixelroot32::graphics::Color::Cyan,       // 3: Light blue\n            pixelroot32::graphics::Color::LightBlue, // 4: Surface\n            pixelroot32::graphics::Color::White,      // 5: Foam\n        };\n\n        // Set custom palette\n        pixelroot32::graphics::setCustomPalette(\n            OCEAN_PALETTE,\n            sizeof(OCEAN_PALETTE) / sizeof(pixelroot32::graphics::Color)\n        );\n\n        // Now all sprites use the ocean palette\n    }\n};\n
    "},{"location":"manual/advanced_graphics/color_palettes/#color-constants","title":"Color Constants","text":"

    PixelRoot32 provides predefined color constants:

    namespace pixelroot32::graphics {\n    Color::Black\n    Color::White\n    Color::Red\n    Color::Green\n    Color::Blue\n    Color::Yellow\n    Color::Cyan\n    Color::Magenta\n    Color::DarkGray\n    Color::LightGray\n    Color::Navy\n    Color::DarkGreen\n    Color::DarkRed\n    Color::Brown\n    Color::Purple\n    Color::Orange\n    Color::Pink\n    Color::Gold\n    Color::LightBlue\n    Color::LightGreen\n    Color::LightRed\n    Color::Transparent\n}\n
    "},{"location":"manual/advanced_graphics/color_palettes/#best-practices","title":"Best Practices","text":""},{"location":"manual/advanced_graphics/color_palettes/#palette-selection","title":"Palette Selection","text":"
    • Match game style: Choose palette that fits your game's theme
    • Test on hardware: Colors may look different on ESP32 display
    • Consider contrast: Ensure sprites are visible against background
    • Consistency: Stick with one palette per scene (or use dual mode)
    "},{"location":"manual/advanced_graphics/color_palettes/#dual-palette-mode_1","title":"Dual Palette Mode","text":"
    • Use sparingly: Not all games need dual palettes
    • Test combinations: Some palette combinations work better than others
    • Clear separation: Use for clear visual distinction between layers
    • Performance: No performance cost, use freely
    "},{"location":"manual/advanced_graphics/color_palettes/#custom-palettes_1","title":"Custom Palettes","text":"
    • Limit colors: Keep palette size reasonable (8-16 colors)
    • Plan ahead: Design palette before creating sprites
    • Test thoroughly: Verify colors work well together
    • Document: Comment your palette choices
    "},{"location":"manual/advanced_graphics/color_palettes/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/advanced_graphics/color_palettes/#palette-switching","title":"Palette Switching","text":"
    class GameScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Set initial palette\n        pixelroot32::graphics::setPalette(\n            pixelroot32::graphics::PaletteType::NES\n        );\n    }\n\n    void changeToNightMode() {\n        // Switch to darker palette\n        pixelroot32::graphics::setPalette(\n            pixelroot32::graphics::PaletteType::GB\n        );\n    }\n\n    void changeToDayMode() {\n        // Switch to brighter palette\n        pixelroot32::graphics::setPalette(\n            pixelroot32::graphics::PaletteType::PICO8\n        );\n    }\n};\n
    "},{"location":"manual/advanced_graphics/color_palettes/#theme-based-palettes","title":"Theme-Based Palettes","text":"
    namespace GamePalettes {\n    // Forest theme\n    static const pixelroot32::graphics::Color FOREST[] = {\n        Color::Black,\n        Color::DarkGreen,\n        Color::Green,\n        Color::LightGreen,\n        Color::Brown,\n        Color::Yellow\n    };\n\n    // Desert theme\n    static const pixelroot32::graphics::Color DESERT[] = {\n        Color::Black,\n        Color::Brown,\n        Color::Yellow,\n        Color::Gold,\n        Color::Orange,\n        Color::White\n    };\n\n    // Ocean theme\n    static const pixelroot32::graphics::Color OCEAN[] = {\n        Color::Black,\n        Color::Navy,\n        Color::Blue,\n        Color::Cyan,\n        Color::LightBlue,\n        Color::White\n    };\n}\n
    "},{"location":"manual/advanced_graphics/color_palettes/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/advanced_graphics/color_palettes/#colors-not-changing","title":"Colors Not Changing","text":"
    • Verify setPalette() is called before drawing
    • Check palette is set in init(), not update()
    • Ensure dual palette mode is enabled if using separate palettes
    • Verify Color constants are from correct namespace
    "},{"location":"manual/advanced_graphics/color_palettes/#colors-look-wrong-on-hardware","title":"Colors Look Wrong on Hardware","text":"
    • ESP32 displays may render colors differently
    • Test on actual hardware, not just PC
    • Adjust palette colors if needed
    • Consider display calibration
    "},{"location":"manual/advanced_graphics/color_palettes/#dual-palette-not-working","title":"Dual Palette Not Working","text":"
    • Ensure enableDualPaletteMode() is called first
    • Verify both palettes are set
    • Check that you're drawing in correct context
    • Review renderer documentation
    "},{"location":"manual/advanced_graphics/color_palettes/#next-steps","title":"Next Steps","text":"

    Now that you understand palettes, learn about: - Cameras and Scrolling - Create scrolling levels - Tilemaps - Build levels with tiles - Particles and Effects - Add visual effects

    See also: - API Reference - PaletteDefs - API Reference - Color - Manual - Basic Rendering

    "},{"location":"manual/advanced_graphics/particles_and_effects/","title":"Particles and Effects","text":"

    The particle system allows you to create visual effects like fire, explosions, smoke, and sparks. This guide covers ParticleEmitter, ParticleConfig, and the included presets.

    "},{"location":"manual/advanced_graphics/particles_and_effects/#particleemitter-basics","title":"ParticleEmitter Basics","text":"

    A ParticleEmitter is an Entity that manages a pool of particles to create visual effects.

    "},{"location":"manual/advanced_graphics/particles_and_effects/#creating-a-particle-emitter","title":"Creating a Particle Emitter","text":"
    #include <graphics/particles/ParticleEmitter.h>\n#include <graphics/particles/ParticleConfig.h>\n\n// Create particle configuration\npixelroot32::graphics::particles::ParticleConfig config;\nconfig.startColor = pixelroot32::graphics::Color::Red;\nconfig.endColor = pixelroot32::graphics::Color::Yellow;\nconfig.lifetime = 1.0f; // 1 second\nconfig.speed = 50.0f;\nconfig.gravity = -100.0f; // Upward (negative = up)\n\n// Create emitter\npixelroot32::graphics::particles::ParticleEmitter* emitter = \n    new pixelroot32::graphics::particles::ParticleEmitter(100, 100, config);\n\n// Add to scene\naddEntity(emitter);\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#emitting-particles","title":"Emitting Particles","text":"
    // Emit a burst of particles\nemitter->burst(100, 100, 10); // x, y, particle count\n\n// Particles will automatically update and draw\n// No additional code needed!\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#particleconfig","title":"ParticleConfig","text":"

    ParticleConfig defines how particles behave:

    #include <graphics/particles/ParticleConfig.h>\n\npixelroot32::graphics::particles::ParticleConfig config;\n\n// Colors\nconfig.startColor = pixelroot32::graphics::Color::Red;   // Color at spawn\nconfig.endColor = pixelroot32::graphics::Color::Yellow;  // Color at death\n\n// Lifetime\nconfig.lifetime = 0.5f; // Duration in seconds\n\n// Velocity\nconfig.speed = 100.0f;           // Base speed\nconfig.speedVariation = 20.0f;   // Random variation\nconfig.direction = 90.0f;        // Direction in degrees (0 = right, 90 = up)\nconfig.directionVariation = 45.0f; // Random direction spread\n\n// Physics\nconfig.gravity = 200.0f;  // Gravity force (positive = down)\nconfig.friction = 0.95f;   // Friction (0.0 to 1.0, 1.0 = no friction)\n\n// Size\nconfig.startSize = 2;     // Size at spawn (pixels)\nconfig.endSize = 1;       // Size at death\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#complete-config-example","title":"Complete Config Example","text":"
    pixelroot32::graphics::particles::ParticleConfig fireConfig;\n\n// Fire colors (red to yellow)\nfireConfig.startColor = pixelroot32::graphics::Color::Red;\nfireConfig.endColor = pixelroot32::graphics::Color::Yellow;\n\n// Short lifetime\nfireConfig.lifetime = 0.3f;\n\n// Upward movement with variation\nfireConfig.speed = 80.0f;\nfireConfig.speedVariation = 30.0f;\nfireConfig.direction = 90.0f; // Up\nfireConfig.directionVariation = 30.0f; // Spread\n\n// Upward gravity (negative)\nfireConfig.gravity = -50.0f;\n\n// Slight friction\nfireConfig.friction = 0.98f;\n\n// Size\nfireConfig.startSize = 3;\nfireConfig.endSize = 1;\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#built-in-presets","title":"Built-in Presets","text":"

    PixelRoot32 includes several particle presets for common effects:

    "},{"location":"manual/advanced_graphics/particles_and_effects/#fire","title":"Fire","text":"
    #include <graphics/particles/ParticlePresets.h>\n\n// Create fire emitter\npixelroot32::graphics::particles::ParticleEmitter* fire = \n    new pixelroot32::graphics::particles::ParticleEmitter(\n        100, 100,\n        pixelroot32::graphics::particles::ParticlePresets::Fire()\n    );\n\n// Emit continuous fire\nvoid update(unsigned long deltaTime) override {\n    fire->burst(100, 100, 2); // Emit 2 particles per frame\n    Scene::update(deltaTime);\n}\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#explosion","title":"Explosion","text":"
    // Create explosion emitter\npixelroot32::graphics::particles::ParticleEmitter* explosion = \n    new pixelroot32::graphics::particles::ParticleEmitter(\n        100, 100,\n        pixelroot32::graphics::particles::ParticlePresets::Explosion()\n    );\n\n// Emit explosion burst\nexplosion->burst(100, 100, 20); // 20 particles at once\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#sparks","title":"Sparks","text":"
    // Create sparks emitter\npixelroot32::graphics::particles::ParticleEmitter* sparks = \n    new pixelroot32::graphics::particles::ParticleEmitter(\n        100, 100,\n        pixelroot32::graphics::particles::ParticlePresets::Sparks()\n    );\n\n// Emit sparks\nsparks->burst(100, 100, 10);\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#smoke","title":"Smoke","text":"
    // Create smoke emitter\npixelroot32::graphics::particles::ParticleEmitter* smoke = \n    new pixelroot32::graphics::particles::ParticleEmitter(\n        100, 100,\n        pixelroot32::graphics::particles::ParticlePresets::Smoke()\n    );\n\n// Emit smoke\nsmoke->burst(100, 100, 3);\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#dust","title":"Dust","text":"
    // Create dust emitter\npixelroot32::graphics::particles::ParticleEmitter* dust = \n    new pixelroot32::graphics::particles::ParticleEmitter(\n        100, 100,\n        pixelroot32::graphics::particles::ParticlePresets::Dust()\n    );\n\n// Emit dust\ndust->burst(100, 100, 5);\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#complete-example-explosion-effect","title":"Complete Example: Explosion Effect","text":"
    #include <core/Scene.h>\n#include <graphics/particles/ParticleEmitter.h>\n#include <graphics/particles/ParticlePresets.h>\n\nclass ExplosionEffect : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::particles::ParticleEmitter* explosion;\n    bool active = false;\n\npublic:\n    ExplosionEffect()\n        : Entity(0, 0, 1, 1, pixelroot32::core::EntityType::GENERIC) {\n        setRenderLayer(1);\n\n        // Create explosion emitter\n        explosion = new pixelroot32::graphics::particles::ParticleEmitter(\n            0, 0,\n            pixelroot32::graphics::particles::ParticlePresets::Explosion()\n        );\n    }\n\n    void trigger(float x, float y) {\n        active = true;\n        this->x = x;\n        this->y = y;\n\n        // Emit explosion burst\n        explosion->burst(x, y, 25);\n    }\n\n    void update(unsigned long deltaTime) override {\n        explosion->update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        explosion->draw(renderer);\n    }\n};\n\n// Usage in scene\nvoid MyScene::init() override {\n    explosionEffect = new ExplosionEffect();\n    addEntity(explosionEffect);\n}\n\nvoid MyScene::update(unsigned long deltaTime) override {\n    auto& input = engine.getInputManager();\n\n    // Trigger explosion on button press\n    if (input.isButtonPressed(4)) { // Button A\n        explosionEffect->trigger(player->x, player->y);\n    }\n\n    Scene::update(deltaTime);\n}\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#continuous-effects","title":"Continuous Effects","text":"

    For continuous effects like fire or smoke:

    class FireEffect : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::particles::ParticleEmitter* fire;\n    unsigned long emitTimer = 0;\n    const unsigned long EMIT_INTERVAL_MS = 50; // Emit every 50ms\n\npublic:\n    FireEffect(float x, float y)\n        : Entity(x, y, 1, 1, pixelroot32::core::EntityType::GENERIC) {\n        setRenderLayer(1);\n\n        fire = new pixelroot32::graphics::particles::ParticleEmitter(\n            x, y,\n            pixelroot32::graphics::particles::ParticlePresets::Fire()\n        );\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Emit particles continuously\n        emitTimer += deltaTime;\n        if (emitTimer >= EMIT_INTERVAL_MS) {\n            emitTimer -= EMIT_INTERVAL_MS;\n            fire->burst(x, y, 2); // 2 particles per interval\n        }\n\n        fire->update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        fire->draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#custom-particle-effects","title":"Custom Particle Effects","text":"

    Create your own particle effects by customizing ParticleConfig:

    "},{"location":"manual/advanced_graphics/particles_and_effects/#magic-spell-effect","title":"Magic Spell Effect","text":"
    pixelroot32::graphics::particles::ParticleConfig magicConfig;\n\n// Magical colors (purple to cyan)\nmagicConfig.startColor = pixelroot32::graphics::Color::Purple;\nmagicConfig.endColor = pixelroot32::graphics::Color::Cyan;\n\n// Medium lifetime\nmagicConfig.lifetime = 0.8f;\n\n// Outward spread\nmagicConfig.speed = 60.0f;\nmagicConfig.speedVariation = 20.0f;\nmagicConfig.direction = 0.0f; // Right\nmagicConfig.directionVariation = 360.0f; // Full circle\n\n// Slight upward float\nmagicConfig.gravity = -30.0f;\n\n// Low friction (floaty)\nmagicConfig.friction = 0.92f;\n\n// Size\nmagicConfig.startSize = 2;\nmagicConfig.endSize = 1;\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#rain-effect","title":"Rain Effect","text":"
    pixelroot32::graphics::particles::ParticleConfig rainConfig;\n\n// Rain color (light blue)\nrainConfig.startColor = pixelroot32::graphics::Color::LightBlue;\nrainConfig.endColor = pixelroot32::graphics::Color::LightBlue;\n\n// Long lifetime\nrainConfig.lifetime = 2.0f;\n\n// Downward movement\nrainConfig.speed = 150.0f;\nrainConfig.speedVariation = 20.0f;\nrainConfig.direction = 270.0f; // Down\nrainConfig.directionVariation = 5.0f; // Slight angle variation\n\n// Downward gravity\nrainConfig.gravity = 200.0f;\n\n// No friction\nrainConfig.friction = 1.0f;\n\n// Small size\nrainConfig.startSize = 1;\nrainConfig.endSize = 1;\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#best-practices","title":"Best Practices","text":""},{"location":"manual/advanced_graphics/particles_and_effects/#performance","title":"Performance","text":"
    • Limit particle count: Each emitter has MAX_PARTICLES_PER_EMITTER (50)
    • Reuse emitters: Don't create new emitters every frame
    • Disable when not visible: Set isVisible = false when off-screen
    • Limit active emitters: Too many emitters can impact performance
    "},{"location":"manual/advanced_graphics/particles_and_effects/#visual-design","title":"Visual Design","text":"
    • Match game style: Particle effects should fit your game's aesthetic
    • Use appropriate colors: Match particle colors to game palette
    • Test on hardware: ESP32 may render particles differently
    • Keep it simple: Simple effects often look better than complex ones
    "},{"location":"manual/advanced_graphics/particles_and_effects/#timing","title":"Timing","text":"
    • Burst timing: Space out bursts for better visual effect
    • Continuous effects: Use timers to control emission rate
    • Lifetime: Adjust lifetime to match effect duration
    • Cleanup: Particles automatically clean up when lifetime expires
    "},{"location":"manual/advanced_graphics/particles_and_effects/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/advanced_graphics/particles_and_effects/#one-shot-effect","title":"One-Shot Effect","text":"
    class OneShotEffect : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::particles::ParticleEmitter* emitter;\n    bool hasEmitted = false;\n\npublic:\n    void trigger(float x, float y) {\n        if (!hasEmitted) {\n            emitter->burst(x, y, 20);\n            hasEmitted = true;\n        }\n    }\n\n    void reset() {\n        hasEmitted = false;\n    }\n};\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#attached-effect","title":"Attached Effect","text":"
    class AttachedParticleEffect : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::particles::ParticleEmitter* emitter;\n    pixelroot32::core::Actor* target;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        // Update emitter position to follow target\n        emitter->x = target->x;\n        emitter->y = target->y;\n\n        // Emit particles\n        emitter->burst(target->x, target->y, 1);\n\n        emitter->update(deltaTime);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/advanced_graphics/particles_and_effects/#particles-not-appearing","title":"Particles Not Appearing","text":"
    • Verify emitter is added to scene
    • Check particle config is valid
    • Ensure burst() is being called
    • Verify emitter position is on-screen
    "},{"location":"manual/advanced_graphics/particles_and_effects/#performance-issues","title":"Performance Issues","text":"
    • Reduce particle count per burst
    • Limit number of active emitters
    • Use simpler particle configs
    • Disable emitters when not visible
    "},{"location":"manual/advanced_graphics/particles_and_effects/#particles-not-moving","title":"Particles Not Moving","text":"
    • Check gravity value (positive = down, negative = up)
    • Verify speed is not 0
    • Check friction isn't too high (1.0 = no movement)
    • Ensure direction is correct (degrees: 0=right, 90=up, 180=left, 270=down)
    "},{"location":"manual/advanced_graphics/particles_and_effects/#next-steps","title":"Next Steps","text":"

    Now that you understand particles, you've completed the advanced graphics section. Continue with: - Performance Optimization - Optimize your game - Memory Management - Manage memory efficiently - API Reference - Complete API documentation

    See also: - API Reference - ParticleEmitter - API Reference - ParticleConfig - API Reference - ParticlePresets - Manual - Basic Rendering

    "},{"location":"manual/advanced_graphics/sprites_and_animation/","title":"Sprites and Animation","text":"

    This guide covers advanced sprite techniques and animation in PixelRoot32, including different sprite formats, creating animations, and best practices.

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#sprite-formats","title":"Sprite Formats","text":"

    PixelRoot32 supports multiple sprite formats, each optimized for different use cases.

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#1bpp-standard-monochrome","title":"1bpp (Standard, Monochrome)","text":"

    The standard format uses 1 bit per pixel (monochrome). This is the most memory-efficient format:

    #include <graphics/Renderer.h>\n\n// Define sprite data (8x8 example)\nstatic const uint16_t PLAYER_SPRITE_DATA[] = {\n    0b00111100,  // Row 0\n    0b01111110,  // Row 1\n    0b11111111,  // Row 2\n    0b11111111,  // Row 3\n    0b11111111,  // Row 4\n    0b01111110,  // Row 5\n    0b00111100,  // Row 6\n    0b00000000   // Row 7\n};\n\n// Create sprite descriptor\nstatic const pixelroot32::graphics::Sprite PLAYER_SPRITE = {\n    PLAYER_SPRITE_DATA,\n    8,  // width\n    8   // height\n};\n\n// Draw sprite\nrenderer.drawSprite(PLAYER_SPRITE, 100, 100, pixelroot32::graphics::Color::White);\n

    Characteristics: - Most memory-efficient - 1 bit per pixel - Maximum width: 16 pixels - Color applied at draw time - Best for: Simple graphics, retro style

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#2bpp-experimental-4-colors","title":"2bpp (Experimental, 4 Colors)","text":"

    2 bits per pixel allows 4 colors per sprite:

    #ifdef PIXELROOT32_ENABLE_2BPP_SPRITES\n#include <graphics/Renderer.h>\n\n// Define 2bpp sprite data\nstatic const uint8_t COLORFUL_SPRITE_DATA[] = {\n    // Each byte represents 4 pixels (2 bits each)\n    // Format: [pixel3][pixel2][pixel1][pixel0]\n    0x00, 0x11, 0x22, 0x33,  // Row 0\n    0x11, 0x22, 0x33, 0x00,  // Row 1\n    // ... more rows\n};\n\n// Define palette (4 colors)\nstatic const pixelroot32::graphics::Color SPRITE_PALETTE[] = {\n    pixelroot32::graphics::Color::Transparent,\n    pixelroot32::graphics::Color::Red,\n    pixelroot32::graphics::Color::Green,\n    pixelroot32::graphics::Color::Blue\n};\n\n// Create 2bpp sprite\nstatic const pixelroot32::graphics::Sprite2bpp COLORFUL_SPRITE = {\n    COLORFUL_SPRITE_DATA,\n    SPRITE_PALETTE,\n    16,  // width\n    8,   // height\n    4    // palette size\n};\n\n// Draw 2bpp sprite\nrenderer.drawSprite(COLORFUL_SPRITE, 100, 100, false);\n#endif\n

    Characteristics: - 2 bits per pixel (4 colors) - Requires custom palette - More memory than 1bpp - Best for: More colorful sprites without full color

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#4bpp-experimental-16-colors","title":"4bpp (Experimental, 16 Colors)","text":"

    4 bits per pixel allows 16 colors per sprite:

    #ifdef PIXELROOT32_ENABLE_4BPP_SPRITES\n#include <graphics/Renderer.h>\n\n// Define 4bpp sprite data\nstatic const uint8_t RICH_SPRITE_DATA[] = {\n    // Each byte represents 2 pixels (4 bits each)\n    // Format: [pixel1][pixel0]\n    0x01, 0x23, 0x45, 0x67,  // Row 0\n    // ... more rows\n};\n\n// Define palette (16 colors)\nstatic const pixelroot32::graphics::Color RICH_PALETTE[] = {\n    pixelroot32::graphics::Color::Transparent,\n    pixelroot32::graphics::Color::Black,\n    pixelroot32::graphics::Color::DarkGray,\n    // ... 13 more colors\n};\n\n// Create 4bpp sprite\nstatic const pixelroot32::graphics::Sprite4bpp RICH_SPRITE = {\n    RICH_SPRITE_DATA,\n    RICH_PALETTE,\n    16,  // width\n    16,  // height\n    16   // palette size\n};\n\n// Draw 4bpp sprite\nrenderer.drawSprite(RICH_SPRITE, 100, 100, false);\n#endif\n

    Characteristics: - 4 bits per pixel (16 colors) - Requires custom palette - Most memory-intensive - Best for: Detailed sprites with many colors

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#multisprite-multi-layer","title":"MultiSprite (Multi-Layer)","text":"

    MultiSprite combines multiple 1bpp layers to create multi-color sprites:

    #include <graphics/Renderer.h>\n\n// Define layers (each is 1bpp)\nstatic const uint16_t BASE_LAYER_DATA[] = {\n    0b00111100,\n    0b01111110,\n    0b11111111,\n    0b11111111,\n    0b11111111,\n    0b01111110,\n    0b00111100,\n    0b00000000\n};\n\nstatic const uint16_t HIGHLIGHT_LAYER_DATA[] = {\n    0b00000000,\n    0b00011000,\n    0b00111100,\n    0b00111100,\n    0b00111100,\n    0b00011000,\n    0b00000000,\n    0b00000000\n};\n\n// Create layers\nstatic const pixelroot32::graphics::SpriteLayer LAYERS[] = {\n    { BASE_LAYER_DATA, pixelroot32::graphics::Color::Blue },      // Base layer\n    { HIGHLIGHT_LAYER_DATA, pixelroot32::graphics::Color::Cyan }  // Highlight layer\n};\n\n// Create MultiSprite\nstatic const pixelroot32::graphics::MultiSprite PLAYER_MULTI = {\n    8,      // width\n    8,      // height\n    LAYERS, // layers array\n    2       // layer count\n};\n\n// Draw MultiSprite\nrenderer.drawSprite(PLAYER_MULTI, 100, 100, false);\n

    Characteristics: - Combines multiple 1bpp layers - Each layer can have different color - Layers drawn in order (first = bottom) - Best for: Complex sprites with highlights, outlines, etc.

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#creating-sprites","title":"Creating Sprites","text":""},{"location":"manual/advanced_graphics/sprites_and_animation/#manual-creation-1bpp","title":"Manual Creation (1bpp)","text":"

    For simple sprites, you can create them manually:

    // 8x8 sprite: Simple circle\nstatic const uint16_t CIRCLE_SPRITE_DATA[] = {\n    0b00111100,  //   ####\n    0b01111110,  //  ######\n    0b11111111,  // ########\n    0b11111111,  // ########\n    0b11111111,  // ########\n    0b11111111,  // ########\n    0b01111110,  //  ######\n    0b00111100   //   ####\n};\n\nstatic const pixelroot32::graphics::Sprite CIRCLE_SPRITE = {\n    CIRCLE_SPRITE_DATA,\n    8,\n    8\n};\n

    Tips: - Use binary notation for clarity - Comment each row to visualize - Keep sprites small (8x8, 16x16) - Reuse sprites when possible

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#using-sprite-compiler","title":"Using Sprite Compiler","text":"

    For complex sprites, use the Sprite Compiler tool (if available) to convert PNG images to sprite data.

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#sprite-animation","title":"Sprite Animation","text":"

    PixelRoot32 uses a step-based animation system that's lightweight and efficient.

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#spriteanimation-structure","title":"SpriteAnimation Structure","text":"
    #include <graphics/Renderer.h>\n\n// Define animation frames\nstatic const uint16_t FRAME1_DATA[] = { /* ... */ };\nstatic const uint16_t FRAME2_DATA[] = { /* ... */ };\nstatic const uint16_t FRAME3_DATA[] = { /* ... */ };\n\nstatic const pixelroot32::graphics::Sprite FRAME1 = { FRAME1_DATA, 8, 8 };\nstatic const pixelroot32::graphics::Sprite FRAME2 = { FRAME2_DATA, 8, 8 };\nstatic const pixelroot32::graphics::Sprite FRAME3 = { FRAME3_DATA, 8, 8 };\n\n// Create animation frames\nstatic const pixelroot32::graphics::SpriteAnimationFrame ANIMATION_FRAMES[] = {\n    { &FRAME1, nullptr },  // Frame 1 (no mask)\n    { &FRAME2, nullptr },  // Frame 2\n    { &FRAME3, nullptr }   // Frame 3\n};\n\n// Create animation\npixelroot32::graphics::SpriteAnimation walkAnimation;\nwalkAnimation.frames = ANIMATION_FRAMES;\nwalkAnimation.frameCount = 3;\nwalkAnimation.current = 0;\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#using-animations-in-actors","title":"Using Animations in Actors","text":"
    #include <core/Actor.h>\n#include <graphics/Renderer.h>\n\nclass AnimatedPlayer : public pixelroot32::core::Actor {\nprivate:\n    pixelroot32::graphics::SpriteAnimation walkAnimation;\n    unsigned long animationTimer = 0;\n    const unsigned long FRAME_DURATION_MS = 100; // 100ms per frame\n\npublic:\n    AnimatedPlayer(float x, float y)\n        : Actor(x, y, 8, 8) {\n        setRenderLayer(1);\n\n        // Initialize animation\n        walkAnimation.frames = WALK_ANIMATION_FRAMES;\n        walkAnimation.frameCount = 3;\n        walkAnimation.current = 0;\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Update animation\n        animationTimer += deltaTime;\n        if (animationTimer >= FRAME_DURATION_MS) {\n            animationTimer -= FRAME_DURATION_MS;\n            walkAnimation.step(); // Advance to next frame\n        }\n\n        // Movement logic...\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Get current frame\n        const pixelroot32::graphics::Sprite* currentFrame = \n            walkAnimation.frames[walkAnimation.current].sprite;\n\n        // Draw current frame\n        renderer.drawSprite(\n            *currentFrame,\n            static_cast<int>(x),\n            static_cast<int>(y),\n            pixelroot32::graphics::Color::White,\n            false // flipX\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Handle collision\n    }\n};\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#animation-control","title":"Animation Control","text":"
    // Reset animation to first frame\nwalkAnimation.reset();\n\n// Step to next frame (loops automatically)\nwalkAnimation.step();\n\n// Get current frame\nconst pixelroot32::graphics::Sprite* frame = \n    walkAnimation.frames[walkAnimation.current].sprite;\n\n// Check if animation is at specific frame\nif (walkAnimation.current == 0) {\n    // At first frame\n}\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#multiple-animations","title":"Multiple Animations","text":"

    For actors with multiple animations (idle, walk, jump):

    class PlayerActor : public pixelroot32::core::Actor {\nprivate:\n    enum class AnimationState {\n        IDLE,\n        WALK,\n        JUMP\n    };\n\n    AnimationState currentState = AnimationState::IDLE;\n    pixelroot32::graphics::SpriteAnimation idleAnim;\n    pixelroot32::graphics::SpriteAnimation walkAnim;\n    pixelroot32::graphics::SpriteAnimation jumpAnim;\n\n    pixelroot32::graphics::SpriteAnimation* getCurrentAnimation() {\n        switch (currentState) {\n            case AnimationState::IDLE: return &idleAnim;\n            case AnimationState::WALK: return &walkAnim;\n            case AnimationState::JUMP: return &jumpAnim;\n        }\n        return &idleAnim;\n    }\n\npublic:\n    void update(unsigned long deltaTime) override {\n        // Update current animation\n        auto* anim = getCurrentAnimation();\n        animationTimer += deltaTime;\n        if (animationTimer >= FRAME_DURATION_MS) {\n            animationTimer -= FRAME_DURATION_MS;\n            anim->step();\n        }\n\n        // Change animation state based on game logic\n        if (isMoving) {\n            if (currentState != AnimationState::WALK) {\n                currentState = AnimationState::WALK;\n                walkAnim.reset();\n            }\n        } else if (isJumping) {\n            if (currentState != AnimationState::JUMP) {\n                currentState = AnimationState::JUMP;\n                jumpAnim.reset();\n            }\n        } else {\n            if (currentState != AnimationState::IDLE) {\n                currentState = AnimationState::IDLE;\n                idleAnim.reset();\n            }\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        auto* anim = getCurrentAnimation();\n        const auto* frame = anim->frames[anim->current].sprite;\n        renderer.drawSprite(*frame, static_cast<int>(x), static_cast<int>(y), \n            pixelroot32::graphics::Color::White);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#sprite-flipping","title":"Sprite Flipping","text":"

    Flip sprites horizontally for facing direction:

    bool facingRight = true;\n\nvoid draw(pixelroot32::graphics::Renderer& renderer) override {\n    renderer.drawSprite(\n        PLAYER_SPRITE,\n        static_cast<int>(x),\n        static_cast<int>(y),\n        pixelroot32::graphics::Color::White,\n        !facingRight // Flip if facing left\n    );\n}\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#best-practices","title":"Best Practices","text":""},{"location":"manual/advanced_graphics/sprites_and_animation/#memory-optimization","title":"Memory Optimization","text":"
    • Reuse sprites: Define sprites once, use many times
    • Use 1bpp when possible: Most memory-efficient
    • Store in flash: Use static const to keep in flash memory
    • Limit sprite count: Too many unique sprites can exhaust memory
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#performance","title":"Performance","text":"
    • Pre-calculate animations: Set up animations in init(), not update()
    • Limit active animations: Only animate visible entities
    • Use appropriate formats: Don't use 4bpp if 1bpp works
    • Batch similar sprites: Draw similar sprites together
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#organization","title":"Organization","text":"
    • Group related sprites: Keep sprite data together
    • Use meaningful names: Name sprites clearly
    • Document complex sprites: Comment sprite bit patterns
    • Create sprite libraries: Reusable sprite collections
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/advanced_graphics/sprites_and_animation/#sprite-sheet-pattern","title":"Sprite Sheet Pattern","text":"

    Organize multiple sprites in arrays:

    namespace PlayerSprites {\n    static const uint16_t IDLE_FRAME1[] = { /* ... */ };\n    static const uint16_t IDLE_FRAME2[] = { /* ... */ };\n    static const uint16_t WALK_FRAME1[] = { /* ... */ };\n    static const uint16_t WALK_FRAME2[] = { /* ... */ };\n\n    static const pixelroot32::graphics::Sprite IDLE[] = {\n        { IDLE_FRAME1, 8, 8 },\n        { IDLE_FRAME2, 8, 8 }\n    };\n\n    static const pixelroot32::graphics::Sprite WALK[] = {\n        { WALK_FRAME1, 8, 8 },\n        { WALK_FRAME2, 8, 8 }\n    };\n}\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#animation-helper","title":"Animation Helper","text":"

    Create a helper class for animation management:

    class AnimationController {\nprivate:\n    pixelroot32::graphics::SpriteAnimation* currentAnim;\n    unsigned long timer = 0;\n    unsigned long frameDuration;\n\npublic:\n    void setAnimation(pixelroot32::graphics::SpriteAnimation* anim) {\n        if (currentAnim != anim) {\n            currentAnim = anim;\n            currentAnim->reset();\n            timer = 0;\n        }\n    }\n\n    void update(unsigned long deltaTime) {\n        if (currentAnim) {\n            timer += deltaTime;\n            if (timer >= frameDuration) {\n                timer -= frameDuration;\n                currentAnim->step();\n            }\n        }\n    }\n\n    const pixelroot32::graphics::Sprite* getCurrentFrame() {\n        if (currentAnim && currentAnim->frameCount > 0) {\n            return currentAnim->frames[currentAnim->current].sprite;\n        }\n        return nullptr;\n    }\n};\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/advanced_graphics/sprites_and_animation/#sprites-not-displaying","title":"Sprites Not Displaying","text":"
    • Check sprite data is valid
    • Verify width/height match data
    • Ensure sprite is within screen bounds
    • Check render layer is correct
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#animation-not-playing","title":"Animation Not Playing","text":"
    • Verify animation frames are set
    • Check step() is being called
    • Ensure timer logic is correct
    • Verify frame count matches array size
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#memory-issues","title":"Memory Issues","text":"
    • Reduce sprite count
    • Use 1bpp instead of 2bpp/4bpp
    • Reuse sprites more
    • Check available flash memory
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#next-steps","title":"Next Steps","text":"

    Now that you understand sprites and animation, learn about: - Color Palettes - Use different color schemes - Cameras and Scrolling - Create scrolling levels - Tilemaps - Build levels with tiles

    See also: - API Reference - Sprite - API Reference - SpriteAnimation - Manual - Basic Rendering

    "},{"location":"manual/advanced_graphics/tilemaps/","title":"Tilemaps","text":"

    Tilemaps allow you to build levels efficiently by reusing small tile sprites. This guide covers creating tilemaps, rendering them, and using them with scrolling cameras.

    "},{"location":"manual/advanced_graphics/tilemaps/#what-are-tilemaps","title":"What are Tilemaps?","text":"

    A tilemap is a 2D grid where each cell references a tile sprite. Instead of placing individual sprites, you define which tile appears at each grid position.

    Advantages: - Memory efficient: Reuse tile sprites many times - Easy level design: Edit level data, not code - Fast rendering: Optimized tilemap drawing - Large levels: Create levels bigger than screen - Multiple Bit-Depths: Support for 1bpp, 2bpp, and 4bpp tilemaps for higher graphical fidelity

    "},{"location":"manual/advanced_graphics/tilemaps/#creating-a-tilemap","title":"Creating a Tilemap","text":""},{"location":"manual/advanced_graphics/tilemaps/#1-define-tiles","title":"1. Define Tiles","text":"

    First, create the tile sprites you'll reuse. You can use standard 1bpp sprites or multi-bpp sprites (2bpp/4bpp) if enabled.

    "},{"location":"manual/advanced_graphics/tilemaps/#1bpp-tiles-example","title":"1bpp Tiles Example","text":"
    #include <graphics/Renderer.h>\n\n// Ground tile (solid)\nstatic const uint16_t TILE_GROUND_BITS[] = {\n    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,\n    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF\n};\n\n// Create tile sprites (8x8 tiles)\nstatic const pixelroot32::graphics::Sprite TILES[] = {\n    { TILE_EMPTY_BITS, 8, 8 },  // Index 0: Empty\n    { TILE_GROUND_BITS, 8, 8 }  // Index 1: Ground\n};\n
    "},{"location":"manual/advanced_graphics/tilemaps/#2bpp-tiles-example-multi-color","title":"2bpp Tiles Example (Multi-color)","text":"
    #include <graphics/Renderer.h>\n\n// 2bpp grass tile\nstatic const uint8_t TILE_GRASS_DATA[] = {\n    0b01010101, 0b01010101, // Packed 2bpp data\n    // ...\n};\n\nstatic const Color GRASS_PALETTE[] = {\n    Color::Transparent, Color::DarkGreen, Color::Green, Color::LightGreen\n};\n\nstatic const pixelroot32::graphics::Sprite2bpp TILES_2BPP[] = {\n    { TILE_GRASS_DATA, GRASS_PALETTE, 8, 8, 4 }\n};\n
    "},{"location":"manual/advanced_graphics/tilemaps/#2-create-tile-index-array","title":"2. Create Tile Index Array","text":"

    Define which tile appears at each position:

    // Tilemap dimensions (30 tiles wide, 20 tiles tall)\nstatic const int TILEMAP_WIDTH = 30;\nstatic const int TILEMAP_HEIGHT = 20;\n\n// Array of tile indices (each byte is a tile index)\nstatic uint8_t TILEMAP_INDICES[TILEMAP_WIDTH * TILEMAP_HEIGHT];\n\n// Initialize to empty\nvoid initTilemap() {\n    for (int i = 0; i < TILEMAP_WIDTH * TILEMAP_HEIGHT; i++) {\n        TILEMAP_INDICES[i] = 0; // Empty\n    }\n\n    // Draw ground at bottom\n    int groundRow = TILEMAP_HEIGHT - 1;\n    for (int x = 0; x < TILEMAP_WIDTH; x++) {\n        TILEMAP_INDICES[groundRow * TILEMAP_WIDTH + x] = 1; // Ground tile\n    }\n\n    // Add some walls\n    TILEMAP_INDICES[5 * TILEMAP_WIDTH + 10] = 2; // Wall at (10, 5)\n    TILEMAP_INDICES[5 * TILEMAP_WIDTH + 11] = 2;\n    TILEMAP_INDICES[5 * TILEMAP_WIDTH + 12] = 2;\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#3-create-tilemap-structure","title":"3. Create TileMap Structure","text":"
    #include <graphics/Renderer.h>\n\nstatic pixelroot32::graphics::TileMap myTileMap = {\n    TILEMAP_INDICES,                    // indices array\n    TILEMAP_WIDTH,                      // width (in tiles)\n    TILEMAP_HEIGHT,                     // height (in tiles)\n    TILES,                              // tiles array\n    8,                                  // tile width (pixels)\n    8,                                  // tile height (pixels)\n    sizeof(TILES) / sizeof(pixelroot32::graphics::Sprite) // tile count\n};\n
    "},{"location":"manual/advanced_graphics/tilemaps/#rendering-tilemaps","title":"Rendering Tilemaps","text":""},{"location":"manual/advanced_graphics/tilemaps/#basic-rendering","title":"Basic Rendering","text":"
    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // 1bpp Tilemap (requires a color)\n    renderer.drawTileMap(\n        myTileMap,\n        0, 0,\n        pixelroot32::graphics::Color::White\n    );\n\n    // 2bpp/4bpp Tilemap (colors are in the sprite palettes)\n    renderer.drawTileMap(myTileMap2bpp, 0, 100);\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#with-camerascrolling","title":"With Camera/Scrolling","text":"
    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Apply camera first\n    camera.apply(renderer);\n\n    // Draw tilemap (camera offset is automatically applied)\n    renderer.drawTileMap(\n        myTileMap,\n        0,                              // World position (0, 0)\n        0,\n        pixelroot32::graphics::Color::White\n    );\n\n    // Draw game objects\n    Scene::draw(renderer);\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#complete-example-platformer-level","title":"Complete Example: Platformer Level","text":"
    #include <core/Scene.h>\n#include <graphics/Renderer.h>\n#include <graphics/Camera2D.h>\n\nclass PlatformerLevel : public pixelroot32::core::Scene {\nprivate:\n    static const int TILE_SIZE = 8;\n    static const int TILEMAP_WIDTH = 100;  // 800 pixels wide\n    static const int TILEMAP_HEIGHT = 30;   // 240 pixels tall\n\n    // Tile definitions\n    static const uint16_t TILE_EMPTY_BITS[] = {\n        0x0000, 0x0000, 0x0000, 0x0000,\n        0x0000, 0x0000, 0x0000, 0x0000\n    };\n\n    static const uint16_t TILE_GROUND_BITS[] = {\n        0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,\n        0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF\n    };\n\n    static const uint16_t TILE_GRASS_BITS[] = {\n        0x0000, 0x0000, 0x0000, 0x0000,\n        0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF\n    };\n\n    static const pixelroot32::graphics::Sprite TILES[] = {\n        { TILE_EMPTY_BITS, TILE_SIZE, TILE_SIZE },  // 0: Empty\n        { TILE_GROUND_BITS, TILE_SIZE, TILE_SIZE }, // 1: Ground\n        { TILE_GRASS_BITS, TILE_SIZE, TILE_SIZE }   // 2: Grass top\n    };\n\n    static uint8_t LEVEL_INDICES[TILEMAP_WIDTH * TILEMAP_HEIGHT];\n\n    pixelroot32::graphics::TileMap levelTileMap;\n    pixelroot32::graphics::Camera2D camera;\n\npublic:\n    PlatformerLevel() \n        : camera(240, 240) {\n        // Initialize tilemap structure\n        levelTileMap = {\n            LEVEL_INDICES,\n            TILEMAP_WIDTH,\n            TILEMAP_HEIGHT,\n            TILES,\n            TILE_SIZE,\n            TILE_SIZE,\n            sizeof(TILES) / sizeof(pixelroot32::graphics::Sprite)\n        };\n    }\n\n    void init() override {\n        // Initialize all tiles to empty\n        for (int i = 0; i < TILEMAP_WIDTH * TILEMAP_HEIGHT; i++) {\n            LEVEL_INDICES[i] = 0;\n        }\n\n        // Create ground level\n        int groundY = TILEMAP_HEIGHT - 1;\n        for (int x = 0; x < TILEMAP_WIDTH; x++) {\n            LEVEL_INDICES[groundY * TILEMAP_WIDTH + x] = 1; // Ground\n        }\n\n        // Add grass on top of ground\n        int grassY = groundY - 1;\n        for (int x = 0; x < TILEMAP_WIDTH; x++) {\n            LEVEL_INDICES[grassY * TILEMAP_WIDTH + x] = 2; // Grass\n        }\n\n        // Add platforms\n        // Platform 1: x=10 to x=15, y=20\n        for (int x = 10; x < 16; x++) {\n            LEVEL_INDICES[20 * TILEMAP_WIDTH + x] = 1; // Ground tile\n        }\n\n        // Platform 2: x=30 to x=35, y=15\n        for (int x = 30; x < 36; x++) {\n            LEVEL_INDICES[15 * TILEMAP_WIDTH + x] = 1;\n        }\n\n        // Set camera boundaries\n        camera.setBounds(0, TILEMAP_WIDTH * TILE_SIZE - 240);\n        camera.setVerticalBounds(0, TILEMAP_HEIGHT * TILE_SIZE - 240);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Follow player (example)\n        // camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Apply camera\n        camera.apply(renderer);\n\n        // Draw tilemap\n        renderer.drawTileMap(\n            levelTileMap,\n            0, 0,\n            pixelroot32::graphics::Color::White\n        );\n\n        // Draw game objects\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/tilemaps/#tilemap-with-scroll","title":"Tilemap with Scroll","text":"

    For scrolling levels, combine tilemaps with cameras:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Apply camera (handles scrolling)\n    camera.apply(renderer);\n\n    // Draw tilemap (automatically scrolled by camera)\n    renderer.drawTileMap(\n        levelTileMap,\n        0, 0,\n        pixelroot32::graphics::Color::White\n    );\n\n    // Draw entities (also scrolled)\n    Scene::draw(renderer);\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#optimizing-tilemap-rendering","title":"Optimizing Tilemap Rendering","text":""},{"location":"manual/advanced_graphics/tilemaps/#viewport-culling","title":"Viewport Culling","text":"

    Only draw visible tiles:

    void drawTileMapOptimized(\n    pixelroot32::graphics::Renderer& renderer,\n    const pixelroot32::graphics::TileMap& tileMap,\n    int offsetX, int offsetY,\n    pixelroot32::graphics::Color color\n) {\n    int screenWidth = renderer.getWidth();\n    int screenHeight = renderer.getHeight();\n\n    // Calculate which tiles are visible\n    int startTileX = (offsetX < 0) ? (-offsetX / tileMap.tileWidth) : 0;\n    int startTileY = (offsetY < 0) ? (-offsetY / tileMap.tileHeight) : 0;\n    int endTileX = startTileX + (screenWidth / tileMap.tileWidth) + 1;\n    int endTileY = startTileY + (screenHeight / tileMap.tileHeight) + 1;\n\n    // Clamp to tilemap bounds\n    if (startTileX < 0) startTileX = 0;\n    if (startTileY < 0) startTileY = 0;\n    if (endTileX > tileMap.width) endTileX = tileMap.width;\n    if (endTileY > tileMap.height) endTileY = tileMap.height;\n\n    // Draw only visible tiles\n    for (int ty = startTileY; ty < endTileY; ty++) {\n        for (int tx = startTileX; tx < endTileX; tx++) {\n            uint8_t tileIndex = tileMap.indices[ty * tileMap.width + tx];\n            if (tileIndex < tileMap.tileCount) {\n                int x = tx * tileMap.tileWidth + offsetX;\n                int y = ty * tileMap.tileHeight + offsetY;\n                renderer.drawSprite(\n                    tileMap.tiles[tileIndex],\n                    x, y,\n                    color,\n                    false\n                );\n            }\n        }\n    }\n}\n

    Note: The built-in drawTileMap() already performs viewport culling, so you typically don't need to implement this yourself.

    "},{"location":"manual/advanced_graphics/tilemaps/#best-practices","title":"Best Practices","text":""},{"location":"manual/advanced_graphics/tilemaps/#tile-design","title":"Tile Design","text":"
    • Keep tiles small: 8x8 or 16x16 pixels work best
    • Reuse tiles: Design tiles that can be used in multiple ways
    • Consistent style: All tiles should match visually
    • Limit tile count: Too many unique tiles uses more memory
    "},{"location":"manual/advanced_graphics/tilemaps/#level-design","title":"Level Design","text":"
    • Use indices efficiently: 0 = empty, 1+ = different tiles
    • Plan layout: Design level on paper/grid first
    • Test on hardware: Large tilemaps may impact performance
    • Optimize data: Use compact level data format
    "},{"location":"manual/advanced_graphics/tilemaps/#performance","title":"Performance","text":"
    • Limit tilemap size: Very large tilemaps can be slow
    • Use appropriate tile size: Smaller tiles = more tiles to draw
    • Combine with culling: Only draw visible area
    • Test scrolling: Ensure smooth scrolling performance
    "},{"location":"manual/advanced_graphics/tilemaps/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/advanced_graphics/tilemaps/#level-data-in-code","title":"Level Data in Code","text":"
    // Define level as 2D array (easier to read)\nstatic const uint8_t LEVEL_DATA[][TILEMAP_WIDTH] = {\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, // Ground\n};\n\n// Copy to tilemap indices\nvoid loadLevel() {\n    for (int y = 0; y < TILEMAP_HEIGHT; y++) {\n        for (int x = 0; x < TILEMAP_WIDTH; x++) {\n            TILEMAP_INDICES[y * TILEMAP_WIDTH + x] = LEVEL_DATA[y][x];\n        }\n    }\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#collision-detection-with-tilemaps","title":"Collision Detection with Tilemaps","text":"
    bool isTileSolid(int tileX, int tileY) {\n    if (tileX < 0 || tileX >= TILEMAP_WIDTH ||\n        tileY < 0 || tileY >= TILEMAP_HEIGHT) {\n        return true; // Out of bounds = solid\n    }\n\n    uint8_t tileIndex = TILEMAP_INDICES[tileY * TILEMAP_WIDTH + tileX];\n    return tileIndex != 0; // 0 = empty, others = solid\n}\n\nbool checkCollision(float x, float y, int width, int height) {\n    // Convert world position to tile coordinates\n    int tileX1 = static_cast<int>(x) / TILE_SIZE;\n    int tileY1 = static_cast<int>(y) / TILE_SIZE;\n    int tileX2 = static_cast<int>(x + width) / TILE_SIZE;\n    int tileY2 = static_cast<int>(y + height) / TILE_SIZE;\n\n    // Check all tiles actor overlaps\n    for (int ty = tileY1; ty <= tileY2; ty++) {\n        for (int tx = tileX1; tx <= tileX2; tx++) {\n            if (isTileSolid(tx, ty)) {\n                return true; // Collision!\n            }\n        }\n    }\n\n    return false; // No collision\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/advanced_graphics/tilemaps/#tiles-not-appearing","title":"Tiles Not Appearing","text":"
    • Verify tile indices are correct (0 = first tile, 1 = second, etc.)
    • Check tilemap dimensions match indices array size
    • Ensure tiles array has enough entries
    • Verify tile size matches sprite size
    "},{"location":"manual/advanced_graphics/tilemaps/#performance-issues","title":"Performance Issues","text":"
    • Reduce tilemap size
    • Use smaller tiles
    • Limit number of unique tiles
    • Test viewport culling
    "},{"location":"manual/advanced_graphics/tilemaps/#scrolling-problems","title":"Scrolling Problems","text":"
    • Ensure camera is applied before drawing tilemap
    • Check tilemap position matches camera offset
    • Verify tilemap boundaries are correct
    • Test with simple tilemap first
    "},{"location":"manual/advanced_graphics/tilemaps/#next-steps","title":"Next Steps","text":"

    Now that you understand tilemaps, learn about: - Particles and Effects - Add visual effects - Cameras and Scrolling - Combine with scrolling - Performance Optimization - Optimize rendering

    See also: - API Reference - TileMap - API Reference - Renderer - Manual - Cameras and Scrolling

    "},{"location":"manual/game_development/audio/","title":"Audio","text":"

    PixelRoot32 includes a complete NES-like audio system with 4 channels for sound effects and background music. This guide shows you how to add sound and music to your games.

    "},{"location":"manual/game_development/audio/#audio-configuration","title":"Audio Configuration","text":"

    Before using audio, you need to configure an AudioBackend. This is done when creating the Engine:

    "},{"location":"manual/game_development/audio/#esp32-internal-dac","title":"ESP32: Internal DAC","text":"
    #include <drivers/esp32/ESP32_DAC_AudioBackend.h>\n\nconst int DAC_PIN = 25; // GPIO 25 or 26\npr32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(DAC_PIN, 11025);\n\npr32::audio::AudioConfig audioConfig(&audioBackend, audioBackend.getSampleRate());\n
    "},{"location":"manual/game_development/audio/#esp32-external-i2s-dac","title":"ESP32: External I2S DAC","text":"
    #include <drivers/esp32/ESP32_I2S_AudioBackend.h>\n\nconst int I2S_BCLK = 26;  // Bit clock\nconst int I2S_LRCK = 25;  // Left/Right clock\nconst int I2S_DOUT = 22;  // Data out\n\npr32::drivers::esp32::ESP32_I2S_AudioBackend audioBackend(\n    I2S_BCLK, I2S_LRCK, I2S_DOUT, 22050\n);\n\npr32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n
    "},{"location":"manual/game_development/audio/#native-pc-sdl2","title":"Native (PC): SDL2","text":"
    #include <drivers/native/SDL2_AudioBackend.h>\n\npr32::drivers::native::SDL2_AudioBackend audioBackend(22050, 1024);\n\npr32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n
    "},{"location":"manual/game_development/audio/#sound-effects","title":"Sound Effects","text":"

    Sound effects are created using AudioEvent structures and played through the AudioEngine.

    "},{"location":"manual/game_development/audio/#audioevent-structure","title":"AudioEvent Structure","text":"
    #include <audio/AudioTypes.h>\n\npr32::audio::AudioEvent soundEffect{};\nsoundEffect.type = pr32::audio::WaveType::PULSE;  // Waveform type\nsoundEffect.frequency = 1500.0f;                  // Frequency in Hz\nsoundEffect.duration = 0.12f;                      // Duration in seconds\nsoundEffect.volume = 0.8f;                         // Volume (0.0 to 1.0)\nsoundEffect.duty = 0.5f;                           // Duty cycle (for PULSE only)\n
    "},{"location":"manual/game_development/audio/#wave-types","title":"Wave Types","text":"

    PixelRoot32 supports three wave types:

    • PULSE: Square wave with variable duty cycle
    • Duty cycles: 0.125 (thin), 0.25 (classic NES), 0.5 (symmetric), 0.75 (fat)
    • Good for: Beeps, jumps, UI sounds, leads

    • TRIANGLE: Triangle wave (fixed volume/duty)

    • Softer, smoother sound
    • Good for: Bass lines, pads, background tones

    • NOISE: Pseudo-random noise

    • Harsh, chaotic sound
    • Good for: Explosions, hits, impacts, drums
    "},{"location":"manual/game_development/audio/#playing-sound-effects","title":"Playing Sound Effects","text":"
    // Get the audio engine\nauto& audio = engine.getAudioEngine();\n\n// Create and play a sound\npr32::audio::AudioEvent jumpSound{};\njumpSound.type = pr32::audio::WaveType::PULSE;\njumpSound.frequency = 800.0f;\njumpSound.duration = 0.1f;\njumpSound.volume = 0.7f;\njumpSound.duty = 0.25f;\n\naudio.playEvent(jumpSound);\n
    "},{"location":"manual/game_development/audio/#common-sound-effects","title":"Common Sound Effects","text":"

    Here are some example sound effects you can use:

    namespace SoundEffects {\n    // Jump sound\n    inline pr32::audio::AudioEvent jump() {\n        pr32::audio::AudioEvent evt{};\n        evt.type = pr32::audio::WaveType::PULSE;\n        evt.frequency = 600.0f;\n        evt.duration = 0.1f;\n        evt.volume = 0.7f;\n        evt.duty = 0.25f;\n        return evt;\n    }\n\n    // Coin/collect sound\n    inline pr32::audio::AudioEvent coin() {\n        pr32::audio::AudioEvent evt{};\n        evt.type = pr32::audio::WaveType::PULSE;\n        evt.frequency = 1500.0f;\n        evt.duration = 0.12f;\n        evt.volume = 0.8f;\n        evt.duty = 0.5f;\n        return evt;\n    }\n\n    // Explosion\n    inline pr32::audio::AudioEvent explosion() {\n        pr32::audio::AudioEvent evt{};\n        evt.type = pr32::audio::WaveType::NOISE;\n        evt.frequency = 200.0f;\n        evt.duration = 0.3f;\n        evt.volume = 0.9f;\n        return evt;\n    }\n\n    // Hit/damage\n    inline pr32::audio::AudioEvent hit() {\n        pr32::audio::AudioEvent evt{};\n        evt.type = pr32::audio::WaveType::NOISE;\n        evt.frequency = 300.0f;\n        evt.duration = 0.15f;\n        evt.volume = 0.6f;\n        return evt;\n    }\n}\n\n// Usage\naudio.playEvent(SoundEffects::jump());\n
    "},{"location":"manual/game_development/audio/#background-music","title":"Background Music","text":"

    Background music uses the MusicPlayer system, which sequences notes over time.

    "},{"location":"manual/game_development/audio/#music-notes","title":"Music Notes","text":"

    Music is built from MusicNote structures:

    #include <audio/AudioMusicTypes.h>\n\nusing namespace pr32::audio;\n\nMusicNote note{};\nnote.note = Note::C;        // Musical note (C, D, E, F, G, A, B, or Rest)\nnote.octave = 4;            // Octave (0-8)\nnote.duration = 0.2f;       // Duration in seconds\nnote.volume = 0.7f;         // Volume (0.0 to 1.0)\n
    "},{"location":"manual/game_development/audio/#instrument-presets","title":"Instrument Presets","text":"

    For convenience, use predefined instrument presets:

    using namespace pr32::audio;\n\n// Available presets:\n// - INSTR_PULSE_LEAD: Main lead pulse (octave 4)\n// - INSTR_PULSE_BASS: Bass pulse (octave 3)\n// - INSTR_PULSE_CHIP_HIGH: High-pitched chiptune (octave 5)\n// - INSTR_TRIANGLE_PAD: Soft triangle pad (octave 4)\n
    "},{"location":"manual/game_development/audio/#creating-a-melody","title":"Creating a Melody","text":"
    #include <audio/AudioMusicTypes.h>\n\nusing namespace pr32::audio;\n\n// Define melody notes\nstatic const MusicNote MELODY_NOTES[] = {\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::E, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::G, 0.25f),\n    makeRest(0.10f),  // Rest (silence)\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::E, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::G, 0.25f),\n    makeRest(0.10f),\n};\n\n// Create music track\nstatic const MusicTrack GAME_MUSIC = {\n    MELODY_NOTES,                              // notes array\n    sizeof(MELODY_NOTES) / sizeof(MusicNote),  // note count\n    true,                                       // loop\n    WaveType::PULSE,                           // channel type\n    0.5f                                       // duty cycle\n};\n
    "},{"location":"manual/game_development/audio/#playing-music","title":"Playing Music","text":"
    // Get the music player\nauto& music = engine.getMusicPlayer();\n\n// Play a track\nmusic.play(GAME_MUSIC);\n\n// Control playback\nmusic.stop();   // Stop playback\nmusic.pause();  // Pause (time doesn't advance)\nmusic.resume(); // Resume after pause\n\n// Check status\nif (music.isPlaying()) {\n    // Music is currently playing\n}\n
    "},{"location":"manual/game_development/audio/#music-in-scene","title":"Music in Scene","text":"

    Typically, you start music in your scene's init():

    void MyGameScene::init() override {\n    // Start background music\n    engine.getMusicPlayer().play(GAME_MUSIC);\n\n    // ... rest of initialization\n}\n
    "},{"location":"manual/game_development/audio/#master-volume","title":"Master Volume","text":"

    Control overall volume without changing individual sounds:

    auto& audio = engine.getAudioEngine();\n\n// Set master volume (0.0 to 1.0)\naudio.setMasterVolume(0.5f); // 50% volume\n\n// Get current volume\nfloat currentVolume = audio.getMasterVolume();\n
    "},{"location":"manual/game_development/audio/#complete-example","title":"Complete Example","text":"

    Here's a complete example combining sound effects and music:

    #include <core/Scene.h>\n#include <audio/AudioTypes.h>\n#include <audio/AudioMusicTypes.h>\n\nusing namespace pr32::audio;\n\n// Background music\nstatic const MusicNote GAME_MELODY[] = {\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::E, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::G, 0.25f),\n    makeRest(0.10f),\n};\n\nstatic const MusicTrack BACKGROUND_MUSIC = {\n    GAME_MELODY,\n    sizeof(GAME_MELODY) / sizeof(MusicNote),\n    true,  // loop\n    WaveType::PULSE,\n    0.5f\n};\n\nclass AudioExampleScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Start background music\n        engine.getMusicPlayer().play(BACKGROUND_MUSIC);\n\n        // Set master volume\n        engine.getAudioEngine().setMasterVolume(0.8f);\n    }\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        auto& audio = engine.getAudioEngine();\n\n        // Play sound effect on button press\n        if (input.isButtonPressed(4)) { // Button A\n            AudioEvent jumpSound{};\n            jumpSound.type = WaveType::PULSE;\n            jumpSound.frequency = 800.0f;\n            jumpSound.duration = 0.1f;\n            jumpSound.volume = 0.7f;\n            jumpSound.duty = 0.25f;\n\n            audio.playEvent(jumpSound);\n        }\n\n        Scene::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/game_development/audio/#designing-nes-like-sounds","title":"Designing NES-like Sounds","text":""},{"location":"manual/game_development/audio/#frequency-guidelines","title":"Frequency Guidelines","text":"
    • Low frequencies (200-400 Hz): Bass, impacts, explosions
    • Mid frequencies (400-1000 Hz): Main sounds, jumps, UI
    • High frequencies (1000-2000 Hz): Beeps, coins, pickups
    • Very high (2000+ Hz): Sharp sounds, alerts
    "},{"location":"manual/game_development/audio/#duration-guidelines","title":"Duration Guidelines","text":"
    • Short (0.05-0.1s): UI clicks, small effects
    • Medium (0.1-0.2s): Jumps, hits, pickups
    • Long (0.2-0.5s): Explosions, power-ups, transitions
    "},{"location":"manual/game_development/audio/#duty-cycle-pulse-only","title":"Duty Cycle (PULSE only)","text":"
    • 0.125: Thin, sharp, piercing
    • 0.25: Classic NES lead sound
    • 0.5: Symmetric, full, fat
    • 0.75: Very fat, bass-like
    "},{"location":"manual/game_development/audio/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/audio/#sound-design","title":"Sound Design","text":"
    • Keep sounds short: Long sounds can overlap and cause issues
    • Use appropriate volumes: 0.6-0.8 is usually good for effects
    • Vary frequencies: Don't use the same frequency for everything
    • Test on hardware: ESP32 audio may sound different than PC
    "},{"location":"manual/game_development/audio/#music","title":"Music","text":"
    • Use one channel for music: Leave other channels for SFX
    • Keep melodies simple: Complex melodies can be hard to follow
    • Loop seamlessly: End your melody where it can loop naturally
    • Consider tempo: Faster games need faster music
    "},{"location":"manual/game_development/audio/#performance","title":"Performance","text":"
    • Limit simultaneous sounds: Only 4 channels total
    • Music uses one channel: Plan your SFX accordingly
    • Don't spam sounds: Too many sounds can cause audio glitches
    • Use master volume: Easier than adjusting individual sounds
    "},{"location":"manual/game_development/audio/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/audio/#sound-effect-helper-function","title":"Sound Effect Helper Function","text":"
    void playJumpSound() {\n    auto& audio = engine.getAudioEngine();\n    AudioEvent evt{};\n    evt.type = WaveType::PULSE;\n    evt.frequency = 600.0f;\n    evt.duration = 0.1f;\n    evt.volume = 0.7f;\n    evt.duty = 0.25f;\n    audio.playEvent(evt);\n}\n
    "},{"location":"manual/game_development/audio/#music-state-management","title":"Music State Management","text":"
    class GameScene : public Scene {\n    bool musicStarted = false;\n\n    void init() override {\n        // Don't start music here if scene can be re-initialized\n    }\n\n    void update(unsigned long deltaTime) override {\n        if (!musicStarted) {\n            engine.getMusicPlayer().play(GAME_MUSIC);\n            musicStarted = true;\n        }\n        Scene::update(deltaTime);\n    }\n};\n
    "},{"location":"manual/game_development/audio/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/game_development/audio/#no-sound","title":"No Sound","text":"
    • Check audio backend is configured correctly
    • Verify sample rate matches backend
    • Check master volume is not 0
    • Ensure audio is initialized (engine.init())
    "},{"location":"manual/game_development/audio/#distorted-sound","title":"Distorted Sound","text":"
    • Lower volume levels
    • Reduce sample rate (ESP32 DAC works better at 11025 Hz)
    • Check for too many simultaneous sounds
    • Verify hardware connections (ESP32)
    "},{"location":"manual/game_development/audio/#music-not-playing","title":"Music Not Playing","text":"
    • Check music.isPlaying() status
    • Ensure track is properly defined
    • Verify MusicPlayer is updated (happens automatically)
    • Check that music channel is not being used by SFX
    "},{"location":"manual/game_development/audio/#next-steps","title":"Next Steps","text":"

    Now that you can add audio, learn about: - NES Audio Reference - Advanced audio techniques - Physics and Collisions - Make objects interact - User Interface - Create menus and HUDs

    See also: - API Reference - AudioEngine - API Reference - MusicPlayer - API Reference - Audio Types - Manual - Audio Overview

    "},{"location":"manual/game_development/basic_rendering/","title":"Basic Rendering","text":"

    Rendering is how you draw everything on screen. This guide covers the fundamental drawing operations in PixelRoot32: primitives, sprites, and text.

    "},{"location":"manual/game_development/basic_rendering/#accessing-the-renderer","title":"Accessing the Renderer","text":"

    You can access the renderer in two ways:

    "},{"location":"manual/game_development/basic_rendering/#from-the-engine","title":"From the Engine","text":"
    auto& renderer = engine.getRenderer();\n
    "},{"location":"manual/game_development/basic_rendering/#from-a-scene","title":"From a Scene","text":"
    void MyScene::draw(pixelroot32::graphics::Renderer& renderer) override {\n    // renderer is passed as parameter\n    renderer.drawFilledRectangle(0, 0, 240, 240, Color::Black);\n}\n
    "},{"location":"manual/game_development/basic_rendering/#drawing-primitives","title":"Drawing Primitives","text":""},{"location":"manual/game_development/basic_rendering/#pixels","title":"Pixels","text":"

    Draw a single pixel:

    renderer.drawPixel(100, 100, pixelroot32::graphics::Color::White);\n
    "},{"location":"manual/game_development/basic_rendering/#lines","title":"Lines","text":"

    Draw a line between two points:

    renderer.drawLine(10, 10, 200, 200, pixelroot32::graphics::Color::Red);\n
    "},{"location":"manual/game_development/basic_rendering/#rectangles","title":"Rectangles","text":"

    Draw a rectangle outline:

    renderer.drawRectangle(50, 50, 100, 80, pixelroot32::graphics::Color::Blue);\n

    Draw a filled rectangle:

    renderer.drawFilledRectangle(50, 50, 100, 80, pixelroot32::graphics::Color::Blue);\n
    "},{"location":"manual/game_development/basic_rendering/#circles","title":"Circles","text":"

    Draw a circle outline:

    renderer.drawCircle(120, 120, 30, pixelroot32::graphics::Color::Green);\n

    Draw a filled circle:

    renderer.drawFilledCircle(120, 120, 30, pixelroot32::graphics::Color::Green);\n
    "},{"location":"manual/game_development/basic_rendering/#simple-sprites-1bpp","title":"Simple Sprites (1bpp)","text":"

    Sprites are the primary way to draw game graphics. The standard format is 1bpp (1 bit per pixel), which is memory-efficient and perfect for retro-style graphics.

    "},{"location":"manual/game_development/basic_rendering/#creating-a-sprite-manually","title":"Creating a Sprite Manually","text":"

    A 1bpp sprite is defined as an array of uint16_t, where each value represents one row:

    #include <graphics/Renderer.h>\n\n// Example: 8x8 sprite (8 rows, each row is a uint16_t)\n// Bit 0 = leftmost pixel, bit 7 = rightmost pixel\nstatic const uint16_t MY_SPRITE_DATA[] = {\n    0b00111100,  // Row 0:   ..####..\n    0b01111110,  // Row 1:  .######.\n    0b11111111,  // Row 2:  ########\n    0b11111111,  // Row 3:  ########\n    0b11111111,  // Row 4:  ########\n    0b01111110,  // Row 5:  .######.\n    0b00111100,  // Row 6:   ..####..\n    0b00000000   // Row 7:  ........\n};\n\n// Create sprite descriptor\nstatic const pixelroot32::graphics::Sprite MY_SPRITE = {\n    MY_SPRITE_DATA,  // data pointer\n    8,                // width\n    8                 // height\n};\n
    "},{"location":"manual/game_development/basic_rendering/#drawing-a-sprite","title":"Drawing a Sprite","text":"
    renderer.drawSprite(\n    MY_SPRITE,                                    // sprite\n    100,                                          // x position\n    100,                                          // y position\n    pixelroot32::graphics::Color::White,         // color\n    false                                         // flipX (optional, default false)\n);\n
    "},{"location":"manual/game_development/basic_rendering/#sprite-bit-convention","title":"Sprite Bit Convention","text":"
    • Each uint16_t represents one row
    • Bit 0 (rightmost bit) = leftmost pixel
    • Bit (width - 1) = rightmost pixel
    • 0 = transparent/off, 1 = on (colored)

    Example: For an 8-pixel wide sprite:

    Row value: 0b00111100\nPixels:    ..####..\n           ^      ^\n         bit 7  bit 0 (leftmost)\n

    "},{"location":"manual/game_development/basic_rendering/#flipping-sprites","title":"Flipping Sprites","text":"

    You can flip a sprite horizontally:

    renderer.drawSprite(MY_SPRITE, 100, 100, Color::White, true); // flipped\n
    "},{"location":"manual/game_development/basic_rendering/#text-rendering","title":"Text Rendering","text":"

    PixelRoot32 uses a native bitmap font system for pixel-perfect text rendering.

    "},{"location":"manual/game_development/basic_rendering/#drawing-text","title":"Drawing Text","text":"
    renderer.drawText(\n    \"Hello World!\",                           // text string\n    10,                                       // x position\n    20,                                       // y position\n    pixelroot32::graphics::Color::White,     // color\n    1                                         // size multiplier (1=normal, 2=double, etc.)\n);\n
    "},{"location":"manual/game_development/basic_rendering/#centered-text","title":"Centered Text","text":"
    renderer.drawTextCentered(\n    \"Game Over\",                              // text\n    120,                                      // y position (centered horizontally)\n    pixelroot32::graphics::Color::Red,       // color\n    2                                         // size\n);\n
    "},{"location":"manual/game_development/basic_rendering/#text-size","title":"Text Size","text":"

    The size parameter multiplies the font size: - 1 = normal size (5x7 pixels per character with default font) - 2 = double size (10x14 pixels) - 3 = triple size (15x21 pixels) - etc.

    "},{"location":"manual/game_development/basic_rendering/#supported-characters","title":"Supported Characters","text":"

    The default font (FONT_5X7) supports ASCII characters 32-126: - Letters: A-Z, a-z - Numbers: 0-9 - Symbols: !@#$%^&*()_+-=[]{}|;:'\",.<>?/ etc.

    "},{"location":"manual/game_development/basic_rendering/#render-layers","title":"Render Layers","text":"

    Entities are drawn in order based on their renderLayer property:

    • Layer 0 (Background): Drawn first (behind everything)
    • Layer 1 (Gameplay): Drawn second (main game objects)
    • Layer 2 (UI): Drawn last (on top of everything)

    Set the render layer when creating an entity:

    myEntity->setRenderLayer(0); // Background\nmyEntity->setRenderLayer(1); // Gameplay (default)\nmyEntity->setRenderLayer(2); // UI\n
    "},{"location":"manual/game_development/basic_rendering/#complete-example","title":"Complete Example","text":"

    Here's a complete example that draws various primitives and a sprite:

    #include <core/Scene.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\n// Simple sprite data (8x8 circle)\nstatic const uint16_t CIRCLE_SPRITE_DATA[] = {\n    0b00111100,\n    0b01111110,\n    0b11111111,\n    0b11111111,\n    0b11111111,\n    0b11111111,\n    0b01111110,\n    0b00111100\n};\n\nstatic const pixelroot32::graphics::Sprite CIRCLE_SPRITE = {\n    CIRCLE_SPRITE_DATA,\n    8,\n    8\n};\n\nclass RenderingExampleScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Set palette\n        pixelroot32::graphics::setPalette(\n            pixelroot32::graphics::PaletteType::NES\n        );\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw background (layer 0)\n        renderer.drawFilledRectangle(0, 0, 240, 240, \n            pixelroot32::graphics::Color::Navy);\n\n        // Draw primitives (layer 1)\n        renderer.drawRectangle(10, 10, 100, 80, \n            pixelroot32::graphics::Color::White);\n        renderer.drawFilledCircle(120, 120, 30, \n            pixelroot32::graphics::Color::Red);\n        renderer.drawLine(0, 0, 240, 240, \n            pixelroot32::graphics::Color::Yellow);\n\n        // Draw sprite\n        renderer.drawSprite(CIRCLE_SPRITE, 50, 50, \n            pixelroot32::graphics::Color::Cyan);\n\n        // Draw text (layer 2 - UI)\n        renderer.drawText(\"Score: 100\", 10, 10, \n            pixelroot32::graphics::Color::White, 1);\n        renderer.drawTextCentered(\"Rendering Demo\", 200, \n            pixelroot32::graphics::Color::Yellow, 2);\n\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/game_development/basic_rendering/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/basic_rendering/#performance","title":"Performance","text":"
    • Minimize draw calls: Batch similar operations when possible
    • Use render layers efficiently: Don't mix layers unnecessarily
    • Avoid drawing off-screen: Check bounds before drawing
    • Reuse sprites: Define sprites once, use many times
    "},{"location":"manual/game_development/basic_rendering/#organization","title":"Organization","text":"
    • Define sprites as static const: Keep them in flash memory
    • Use meaningful names: Name your sprites clearly
    • Group related sprites: Organize sprite data logically
    • Document complex sprites: Comment sprite bit patterns if needed
    "},{"location":"manual/game_development/basic_rendering/#text","title":"Text","text":"
    • Avoid frequent text updates: Text rendering has overhead
    • Use appropriate sizes: Larger text uses more memory
    • Cache text dimensions: Use FontManager::textWidth() if needed
    • Keep text simple: Complex formatting is not supported
    "},{"location":"manual/game_development/basic_rendering/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/basic_rendering/#drawing-a-background","title":"Drawing a Background","text":"
    void drawBackground(Renderer& renderer) {\n    // Solid color background\n    renderer.drawFilledRectangle(0, 0, 240, 240, Color::Black);\n\n    // Or use a tilemap (see Advanced Graphics section)\n}\n
    "},{"location":"manual/game_development/basic_rendering/#drawing-a-hud","title":"Drawing a HUD","text":"
    void drawHUD(Renderer& renderer, int score, int lives) {\n    char buffer[32];\n    snprintf(buffer, sizeof(buffer), \"Score: %d\", score);\n    renderer.drawText(buffer, 10, 10, Color::White, 1);\n\n    snprintf(buffer, sizeof(buffer), \"Lives: %d\", lives);\n    renderer.drawText(buffer, 10, 20, Color::White, 1);\n}\n
    "},{"location":"manual/game_development/basic_rendering/#drawing-multiple-sprites","title":"Drawing Multiple Sprites","text":"
    void drawSpriteArray(Renderer& renderer, const Sprite& sprite, \n                     int count, int startX, int startY, int spacing) {\n    for (int i = 0; i < count; i++) {\n        int x = startX + (i * (sprite.width + spacing));\n        renderer.drawSprite(sprite, x, startY, Color::White);\n    }\n}\n
    "},{"location":"manual/game_development/basic_rendering/#next-steps","title":"Next Steps","text":"

    Now that you can draw basic graphics, learn about: - Sprites and Animation - Advanced sprite techniques - Input and Control - Make your game interactive - Palettes - Use different color schemes

    See also: - API Reference - Renderer - API Reference - Sprite - Render Layers

    "},{"location":"manual/game_development/input_and_control/","title":"Input and Control","text":"

    Handling user input is essential for any interactive game. This guide covers how to read and process input from buttons (ESP32) or keyboard (Native) in PixelRoot32.

    "},{"location":"manual/game_development/input_and_control/#input-configuration","title":"Input Configuration","text":"

    Before you can read input, you need to configure the InputManager. This is done when creating the Engine:

    "},{"location":"manual/game_development/input_and_control/#esp32-configuration","title":"ESP32 Configuration","text":"
    #include <input/InputConfig.h>\n\n// InputConfig(buttonCount, UP, DOWN, LEFT, RIGHT, A, B)\npr32::input::InputConfig inputConfig(\n    6,      // Total number of buttons\n    32,     // UP button GPIO pin\n    27,     // DOWN button GPIO pin\n    33,     // LEFT button GPIO pin\n    14,     // RIGHT button GPIO pin\n    13,     // A button GPIO pin\n    12      // B button GPIO pin\n);\n
    "},{"location":"manual/game_development/input_and_control/#native-pc-configuration","title":"Native (PC) Configuration","text":"
    #include <SDL2/SDL.h>\n#include <input/InputConfig.h>\n\n// InputConfig(buttonCount, UP, DOWN, LEFT, RIGHT, A, B)\npr32::input::InputConfig inputConfig(\n    6,                      // Total number of buttons\n    SDL_SCANCODE_UP,        // UP key\n    SDL_SCANCODE_DOWN,      // DOWN key\n    SDL_SCANCODE_LEFT,      // LEFT key\n    SDL_SCANCODE_RIGHT,     // RIGHT key\n    SDL_SCANCODE_SPACE,     // A button (Space)\n    SDL_SCANCODE_RETURN     // B button (Enter)\n);\n
    "},{"location":"manual/game_development/input_and_control/#reading-input","title":"Reading Input","text":"

    Access the InputManager through the Engine:

    auto& input = engine.getInputManager();\n
    "},{"location":"manual/game_development/input_and_control/#input-states","title":"Input States","text":"

    The InputManager provides four different ways to check button state:

    "},{"location":"manual/game_development/input_and_control/#1-isbuttonpressed","title":"1. isButtonPressed()","text":"

    Returns true only on the frame when the button was just pressed:

    if (input.isButtonPressed(4)) { // Button A (index 4)\n    // This code runs only once when button is first pressed\n    jump();\n}\n

    Use for: Actions that should trigger once per press (jump, shoot, select menu item).

    "},{"location":"manual/game_development/input_and_control/#2-isbuttonreleased","title":"2. isButtonReleased()","text":"

    Returns true only on the frame when the button was just released:

    if (input.isButtonReleased(4)) {\n    // This code runs only once when button is released\n    stopCharging();\n}\n

    Use for: Actions that trigger on release (charge attacks, menu confirmation).

    "},{"location":"manual/game_development/input_and_control/#3-isbuttondown","title":"3. isButtonDown()","text":"

    Returns true while the button is currently held down:

    if (input.isButtonDown(2)) { // LEFT button (index 2)\n    // This code runs every frame while button is held\n    playerX -= speed * (deltaTime * 0.001f);\n}\n

    Use for: Continuous actions (movement, holding, charging).

    "},{"location":"manual/game_development/input_and_control/#4-isbuttonclicked","title":"4. isButtonClicked()","text":"

    Returns true when the button was pressed and then released:

    if (input.isButtonClicked(4)) {\n    // This code runs once per click (press + release cycle)\n    toggleMenu();\n}\n

    Use for: Toggle actions, menu selections, click interactions.

    "},{"location":"manual/game_development/input_and_control/#button-indices","title":"Button Indices","text":"

    The button indices correspond to the order in InputConfig:

    • Index 0: UP
    • Index 1: DOWN
    • Index 2: LEFT
    • Index 3: RIGHT
    • Index 4: A button
    • Index 5: B button

    For convenience, you can define constants:

    namespace Buttons {\n    constexpr uint8_t UP = 0;\n    constexpr uint8_t DOWN = 1;\n    constexpr uint8_t LEFT = 2;\n    constexpr uint8_t RIGHT = 3;\n    constexpr uint8_t A = 4;\n    constexpr uint8_t B = 5;\n}\n\n// Usage\nif (input.isButtonPressed(Buttons::A)) {\n    // ...\n}\n
    "},{"location":"manual/game_development/input_and_control/#character-control-example","title":"Character Control Example","text":"

    Here's a complete example of character movement:

    #include <core/Actor.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\nclass PlayerActor : public pixelroot32::core::Actor {\npublic:\n    float speed = 100.0f; // pixels per second\n\n    PlayerActor(float x, float y)\n        : Actor(x, y, 16, 16) {\n        setRenderLayer(1);\n    }\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        float dt = deltaTime * 0.001f; // Convert to seconds\n\n        // Horizontal movement\n        if (input.isButtonDown(Buttons::LEFT)) {\n            x -= speed * dt;\n        }\n        if (input.isButtonDown(Buttons::RIGHT)) {\n            x += speed * dt;\n        }\n\n        // Vertical movement\n        if (input.isButtonDown(Buttons::UP)) {\n            y -= speed * dt;\n        }\n        if (input.isButtonDown(Buttons::DOWN)) {\n            y += speed * dt;\n        }\n\n        // Keep player on screen\n        if (x < 0) x = 0;\n        if (x > 224) x = 224;\n        if (y < 0) y = 0;\n        if (y > 224) y = 224;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x),\n            static_cast<int>(y),\n            width,\n            height,\n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Handle collisions\n    }\n};\n
    "},{"location":"manual/game_development/input_and_control/#jumping-example","title":"Jumping Example","text":"

    For platformer-style jumping:

    class PlatformerPlayer : public pixelroot32::core::PhysicsActor {\npublic:\n    bool canJump = true;\n    float jumpForce = 200.0f;\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        float dt = deltaTime * 0.001f;\n\n        // Horizontal movement\n        float moveSpeed = 80.0f;\n        if (input.isButtonDown(Buttons::LEFT)) {\n            setVelocity(-moveSpeed, vy);\n        } else if (input.isButtonDown(Buttons::RIGHT)) {\n            setVelocity(moveSpeed, vy);\n        } else {\n            setVelocity(0, vy);\n        }\n\n        // Jump (only on press, and only if on ground)\n        if (input.isButtonPressed(Buttons::A) && canJump) {\n            setVelocity(vx, -jumpForce);\n            canJump = false;\n        }\n\n        // Check if on ground (for jump reset)\n        auto collisionInfo = getWorldCollisionInfo();\n        if (collisionInfo.bottom) {\n            canJump = true;\n        }\n\n        PhysicsActor::update(deltaTime);\n    }\n};\n
    "},{"location":"manual/game_development/input_and_control/#shooting-example","title":"Shooting Example","text":"

    For shooting projectiles:

    class ShooterActor : public pixelroot32::core::Actor {\nprivate:\n    bool fireInputReady = true;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n\n        // Shooting (with cooldown)\n        if (input.isButtonPressed(Buttons::A) && fireInputReady) {\n            shoot();\n            fireInputReady = false;\n        }\n\n        // Reset fire input when button is released\n        if (input.isButtonReleased(Buttons::A)) {\n            fireInputReady = true;\n        }\n    }\n\n    void shoot() {\n        // Create projectile\n        // ...\n    }\n};\n
    "},{"location":"manual/game_development/input_and_control/#menu-navigation-example","title":"Menu Navigation Example","text":"

    For menu navigation:

    class MenuScene : public pixelroot32::core::Scene {\nprivate:\n    int selectedIndex = 0;\n    static const int MENU_ITEMS = 3;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n\n        // Navigate menu\n        if (input.isButtonPressed(Buttons::UP)) {\n            selectedIndex--;\n            if (selectedIndex < 0) selectedIndex = MENU_ITEMS - 1;\n        }\n\n        if (input.isButtonPressed(Buttons::DOWN)) {\n            selectedIndex++;\n            if (selectedIndex >= MENU_ITEMS) selectedIndex = 0;\n        }\n\n        // Select menu item\n        if (input.isButtonPressed(Buttons::A)) {\n            selectMenuItem(selectedIndex);\n        }\n\n        Scene::update(deltaTime);\n    }\n};\n
    "},{"location":"manual/game_development/input_and_control/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/input_and_control/#frame-rate-independence","title":"Frame-Rate Independence","text":"

    Always multiply movement by delta time:

    // \u2705 GOOD: Framerate-independent\nx += speed * (deltaTime * 0.001f);\n\n// \u274c BAD: Framerate-dependent\nx += speed;\n
    "},{"location":"manual/game_development/input_and_control/#input-debouncing","title":"Input Debouncing","text":"

    For actions that should only trigger once:

    // Use isButtonPressed() instead of isButtonDown()\nif (input.isButtonPressed(Buttons::A)) {\n    // Triggers once per press\n}\n
    "},{"location":"manual/game_development/input_and_control/#input-buffering","title":"Input Buffering","text":"

    For responsive controls, you can buffer input:

    class InputBuffer {\n    uint8_t bufferedInput = 0;\n\npublic:\n    void update(const InputManager& input) {\n        if (input.isButtonPressed(Buttons::A)) {\n            bufferedInput = Buttons::A;\n        }\n    }\n\n    bool hasBufferedInput() const { return bufferedInput != 0; }\n    uint8_t consumeInput() {\n        uint8_t result = bufferedInput;\n        bufferedInput = 0;\n        return result;\n    }\n};\n
    "},{"location":"manual/game_development/input_and_control/#multiple-input-methods","title":"Multiple Input Methods","text":"

    Support both button presses and held buttons:

    // Allow both tap and hold for rapid fire\nif (input.isButtonPressed(Buttons::A) || \n    (input.isButtonDown(Buttons::A) && rapidFireTimer <= 0)) {\n    shoot();\n    rapidFireTimer = RAPID_FIRE_DELAY;\n}\n
    "},{"location":"manual/game_development/input_and_control/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/input_and_control/#directional-input","title":"Directional Input","text":"
    float getHorizontalInput() {\n    auto& input = engine.getInputManager();\n    float dir = 0.0f;\n    if (input.isButtonDown(Buttons::LEFT)) dir -= 1.0f;\n    if (input.isButtonDown(Buttons::RIGHT)) dir += 1.0f;\n    return dir;\n}\n\nfloat getVerticalInput() {\n    auto& input = engine.getInputManager();\n    float dir = 0.0f;\n    if (input.isButtonDown(Buttons::UP)) dir -= 1.0f;\n    if (input.isButtonDown(Buttons::DOWN)) dir += 1.0f;\n    return dir;\n}\n
    "},{"location":"manual/game_development/input_and_control/#input-state-machine","title":"Input State Machine","text":"

    For complex input handling:

    enum class InputState {\n    IDLE,\n    PRESSED,\n    HELD,\n    RELEASED\n};\n\nInputState getButtonState(const InputManager& input, uint8_t button) {\n    if (input.isButtonPressed(button)) return InputState::PRESSED;\n    if (input.isButtonDown(button)) return InputState::HELD;\n    if (input.isButtonReleased(button)) return InputState::RELEASED;\n    return InputState::IDLE;\n}\n
    "},{"location":"manual/game_development/input_and_control/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/game_development/input_and_control/#button-not-responding","title":"Button Not Responding","text":"
    • Check button indices match your InputConfig
    • Verify GPIO pins (ESP32) or scancodes (Native) are correct
    • Ensure InputManager is being updated (happens automatically in Engine)
    "},{"location":"manual/game_development/input_and_control/#input-feels-laggy","title":"Input Feels Laggy","text":"
    • Ensure you're using deltaTime for movement
    • Check that input is read in update(), not draw()
    • Verify framerate is stable
    "},{"location":"manual/game_development/input_and_control/#multiple-triggers","title":"Multiple Triggers","text":"
    • Use isButtonPressed() instead of isButtonDown() for one-time actions
    • Implement input buffering or cooldown timers
    "},{"location":"manual/game_development/input_and_control/#next-steps","title":"Next Steps","text":"

    Now that you can handle input, learn about: - Audio - Add sound effects and music - Physics and Collisions - Make objects interact - User Interface - Create menus and HUDs

    See also: - API Reference - InputManager - API Reference - InputConfig - Manual - Input Overview

    "},{"location":"manual/game_development/physics_and_collisions/","title":"Physics and Collisions","text":"

    PixelRoot32 provides a physics system for moving objects and collision detection. This guide covers PhysicsActor for automatic physics and the collision system for detecting interactions between objects.

    "},{"location":"manual/game_development/physics_and_collisions/#physicsactor","title":"PhysicsActor","text":"

    A PhysicsActor is an Actor that automatically handles physics: velocity, gravity, friction, and world boundary collisions.

    "},{"location":"manual/game_development/physics_and_collisions/#creating-a-physicsactor","title":"Creating a PhysicsActor","text":"
    #include <core/PhysicsActor.h>\n\nclass Ball : public pixelroot32::core::PhysicsActor {\npublic:\n    Ball(float x, float y, float radius)\n        : PhysicsActor(x, y, radius * 2, radius * 2) {\n        setRenderLayer(1);\n\n        // Set physics properties\n        setRestitution(0.8f);  // Bounciness (0.8 = 80% bounce)\n        setFriction(0.1f);     // Friction (0.1 = slight friction)\n\n        // Set world boundaries\n        setWorldSize(240, 240);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Apply gravity\n        // (PhysicsActor handles this automatically, but you can add custom forces)\n\n        // Call parent update to apply physics\n        PhysicsActor::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        int radius = width / 2;\n        renderer.drawFilledCircle(\n            static_cast<int>(x + radius),\n            static_cast<int>(y + radius),\n            radius,\n            pixelroot32::graphics::Color::White\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Handle collision with other actors\n    }\n\n    void onWorldCollision() override {\n        // Called when hitting world boundaries\n        // You can play a sound effect here, for example\n    }\n};\n
    "},{"location":"manual/game_development/physics_and_collisions/#physics-properties","title":"Physics Properties","text":""},{"location":"manual/game_development/physics_and_collisions/#velocity","title":"Velocity","text":"

    Set the velocity directly:

    ball->setVelocity(100.0f, -50.0f); // Move right at 100 px/s, up at 50 px/s\n

    Or modify existing velocity:

    float vx = ball->vx; // Access velocity components (protected, use getter if needed)\nball->setVelocity(vx + 10.0f, ball->vy); // Accelerate horizontally\n
    "},{"location":"manual/game_development/physics_and_collisions/#restitution-bounciness","title":"Restitution (Bounciness)","text":"

    Controls how much energy is conserved in collisions:

    ball->setRestitution(1.0f);  // Perfect bounce (no energy loss)\nball->setRestitution(0.5f);  // 50% energy loss\nball->setRestitution(0.0f);  // No bounce (stops on impact)\n
    "},{"location":"manual/game_development/physics_and_collisions/#friction","title":"Friction","text":"

    Applies gradual velocity reduction:

    ball->setFriction(0.0f);  // No friction (slides forever)\nball->setFriction(0.5f);  // Moderate friction\nball->setFriction(1.0f);  // High friction (stops quickly)\n
    "},{"location":"manual/game_development/physics_and_collisions/#world-boundaries","title":"World Boundaries","text":"

    Set the playable area:

    // Set world size (used as default boundaries)\nball->setWorldSize(240, 240);\n\n// Or set custom boundaries\npixelroot32::core::LimitRect limits(10, 10, 230, 230); // Left, Top, Right, Bottom\nball->setLimits(limits);\n
    "},{"location":"manual/game_development/physics_and_collisions/#world-collision-detection","title":"World Collision Detection","text":"

    Check if the actor hit world boundaries:

    void update(unsigned long deltaTime) override {\n    PhysicsActor::update(deltaTime);\n\n    auto collisionInfo = getWorldCollisionInfo();\n\n    if (collisionInfo.left || collisionInfo.right) {\n        // Hit side walls\n    }\n\n    if (collisionInfo.top || collisionInfo.bottom) {\n        // Hit top/bottom walls\n    }\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#collision-system","title":"Collision System","text":"

    The collision system detects when Actors overlap and triggers callbacks.

    "},{"location":"manual/game_development/physics_and_collisions/#collision-layers","title":"Collision Layers","text":"

    Use bit flags to organize actors into groups:

    // Define layers (typically in GameLayers.h)\nnamespace Layers {\n    constexpr uint16_t PLAYER = 0x0001;    // Bit 0\n    constexpr uint16_t ENEMY = 0x0002;     // Bit 1\n    constexpr uint16_t PROJECTILE = 0x0004; // Bit 2\n    constexpr uint16_t WALL = 0x0008;      // Bit 3\n    constexpr uint16_t PICKUP = 0x0010;    // Bit 4\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#setting-up-collisions","title":"Setting Up Collisions","text":"

    Configure each actor's collision layer and mask:

    class PlayerActor : public pixelroot32::core::Actor {\npublic:\n    PlayerActor(float x, float y)\n        : Actor(x, y, 16, 16) {\n        // This actor belongs to the PLAYER layer\n        setCollisionLayer(Layers::PLAYER);\n\n        // This actor can collide with ENEMY, WALL, and PICKUP\n        setCollisionMask(Layers::ENEMY | Layers::WALL | Layers::PICKUP);\n    }\n\n    // ... rest of implementation\n};\n\nclass EnemyActor : public pixelroot32::core::Actor {\npublic:\n    EnemyActor(float x, float y)\n        : Actor(x, y, 16, 16) {\n        // This actor belongs to the ENEMY layer\n        setCollisionLayer(Layers::ENEMY);\n\n        // This actor can collide with PLAYER and PROJECTILE\n        setCollisionMask(Layers::PLAYER | Layers::PROJECTILE);\n    }\n\n    // ... rest of implementation\n};\n
    "},{"location":"manual/game_development/physics_and_collisions/#collision-detection","title":"Collision Detection","text":"

    Collisions are automatically detected by the Scene's CollisionSystem. You handle collisions in onCollision():

    void PlayerActor::onCollision(pixelroot32::core::Actor* other) override {\n    // Check what we collided with\n    if (other->isInLayer(Layers::ENEMY)) {\n        // Hit an enemy - take damage\n        takeDamage();\n    } else if (other->isInLayer(Layers::PICKUP)) {\n        // Hit a pickup - collect it\n        collectPickup(other);\n    } else if (other->isInLayer(Layers::WALL)) {\n        // Hit a wall - stop movement\n        stopMovement();\n    }\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#hitbox","title":"Hitbox","text":"

    Define the collision shape:

    pixelroot32::core::Rect getHitBox() override {\n    // Simple AABB (Axis-Aligned Bounding Box)\n    return {x, y, width, height};\n\n    // Or use a smaller hitbox for more forgiving collisions\n    // return {x + 2, y + 2, width - 4, height - 4};\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#complete-example-bouncing-ball","title":"Complete Example: Bouncing Ball","text":"
    #include <core/PhysicsActor.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\nclass BouncingBall : public pixelroot32::core::PhysicsActor {\npublic:\n    BouncingBall(float x, float y, float radius)\n        : PhysicsActor(x, y, radius * 2, radius * 2) {\n        setRenderLayer(1);\n\n        // Physics setup\n        setRestitution(0.9f);  // Very bouncy\n        setFriction(0.05f);    // Low friction\n        setWorldSize(240, 240); // World boundaries\n\n        // Initial velocity\n        setVelocity(50.0f, -30.0f);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Apply gravity\n        float gravity = 200.0f; // pixels per second squared\n        float dt = deltaTime * 0.001f;\n        setVelocity(vx, vy + gravity * dt);\n\n        // Update physics\n        PhysicsActor::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        int radius = width / 2;\n        renderer.drawFilledCircle(\n            static_cast<int>(x + radius),\n            static_cast<int>(y + radius),\n            radius,\n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Bounce off other objects\n        // (PhysicsActor handles world boundaries automatically)\n    }\n\n    void onWorldCollision() override {\n        // Play bounce sound when hitting walls\n        // (Implementation depends on your audio setup)\n    }\n};\n
    "},{"location":"manual/game_development/physics_and_collisions/#complete-example-platformer-player","title":"Complete Example: Platformer Player","text":"
    class PlatformerPlayer : public pixelroot32::core::PhysicsActor {\nprivate:\n    bool onGround = false;\n    float jumpForce = 250.0f;\n    float moveSpeed = 100.0f;\n\npublic:\n    PlatformerPlayer(float x, float y)\n        : PhysicsActor(x, y, 16, 16) {\n        setRenderLayer(1);\n        setFriction(0.3f);  // Ground friction\n        setWorldSize(240, 240);\n\n        // Collision setup\n        setCollisionLayer(Layers::PLAYER);\n        setCollisionMask(Layers::ENEMY | Layers::PLATFORM);\n    }\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        float dt = deltaTime * 0.001f;\n\n        // Horizontal movement\n        float moveDir = 0.0f;\n        if (input.isButtonDown(Buttons::LEFT)) moveDir -= 1.0f;\n        if (input.isButtonDown(Buttons::RIGHT)) moveDir += 1.0f;\n\n        setVelocity(moveDir * moveSpeed, vy);\n\n        // Apply gravity\n        float gravity = 300.0f;\n        setVelocity(vx, vy + gravity * dt);\n\n        // Jump\n        if (input.isButtonPressed(Buttons::A) && onGround) {\n            setVelocity(vx, -jumpForce);\n            onGround = false;\n        }\n\n        // Update physics\n        PhysicsActor::update(deltaTime);\n\n        // Check if on ground\n        auto collisionInfo = getWorldCollisionInfo();\n        onGround = collisionInfo.bottom;\n\n        // Also check collision with platforms\n        // (This would be handled in onCollision)\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        if (other->isInLayer(Layers::PLATFORM)) {\n            // Land on platform\n            auto platformRect = other->getHitBox();\n            if (y + height <= platformRect.y + 5) { // Within 5 pixels of top\n                y = platformRect.y - height;\n                vy = 0;\n                onGround = true;\n            }\n        } else if (other->isInLayer(Layers::ENEMY)) {\n            // Hit enemy\n            takeDamage();\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x),\n            static_cast<int>(y),\n            width,\n            height,\n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n};\n
    "},{"location":"manual/game_development/physics_and_collisions/#sweep-tests","title":"Sweep Tests","text":"

    For fast-moving projectiles, use sweep tests to detect collisions between positions:

    #include <physics/CollisionPrimitives.h>\n\nbool checkProjectileHit(PhysicsActor* projectile, Actor* target) {\n    // Get previous and current positions\n    float prevX = projectile->x - (projectile->vx * deltaTime * 0.001f);\n    float prevY = projectile->y - (projectile->vy * deltaTime * 0.001f);\n\n    // Create circles for sweep test\n    pixelroot32::physics::Circle startCircle;\n    startCircle.x = prevX + projectile->width / 2;\n    startCircle.y = prevY + projectile->height / 2;\n    startCircle.radius = projectile->width / 2;\n\n    pixelroot32::physics::Circle endCircle;\n    endCircle.x = projectile->x + projectile->width / 2;\n    endCircle.y = projectile->y + projectile->height / 2;\n    endCircle.radius = projectile->width / 2;\n\n    // Get target rectangle\n    auto targetRect = target->getHitBox();\n\n    // Perform sweep test\n    float tHit;\n    if (pixelroot32::physics::sweepCircleVsRect(\n        startCircle, endCircle, targetRect, tHit)) {\n        // Collision detected at time tHit (0.0 = at start, 1.0 = at end)\n        return true;\n    }\n\n    return false;\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/physics_and_collisions/#collision-layers_1","title":"Collision Layers","text":"
    • Plan your layers: Design the layer system before coding
    • Use bit flags: Makes combining layers easy with | operator
    • Keep it simple: Don't create too many layers
    • Document layers: Create a GameLayers.h file with all definitions
    "},{"location":"manual/game_development/physics_and_collisions/#physics","title":"Physics","text":"
    • Use appropriate values: Test gravity, speed, and forces
    • Frame-rate independence: Always use deltaTime
    • Limit world size: Keep boundaries reasonable
    • Test on hardware: Physics may behave differently on ESP32 vs PC
    "},{"location":"manual/game_development/physics_and_collisions/#performance","title":"Performance","text":"
    • Limit active actors: Fewer actors = faster collision checks
    • Use layers efficiently: Reduce unnecessary collision pairs
    • Simple hitboxes: AABB is fast, complex shapes are slow
    • Sweep tests sparingly: Only for fast-moving objects
    "},{"location":"manual/game_development/physics_and_collisions/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/physics_and_collisions/#collision-layer-helper","title":"Collision Layer Helper","text":"
    namespace CollisionLayers {\n    constexpr uint16_t PLAYER = 0x0001;\n    constexpr uint16_t ENEMY = 0x0002;\n    constexpr uint16_t PROJECTILE = 0x0004;\n    constexpr uint16_t WALL = 0x0008;\n    constexpr uint16_t PICKUP = 0x0010;\n\n    // Helper to check if actor is in specific layer\n    bool isPlayer(Actor* actor) {\n        return actor->isInLayer(PLAYER);\n    }\n\n    bool isEnemy(Actor* actor) {\n        return actor->isInLayer(ENEMY);\n    }\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#platform-collision","title":"Platform Collision","text":"
    void PlatformerPlayer::onCollision(Actor* other) override {\n    if (other->isInLayer(Layers::PLATFORM)) {\n        auto platform = other->getHitBox();\n\n        // Check if landing on top of platform\n        float playerBottom = y + height;\n        float platformTop = platform.y;\n\n        if (playerBottom <= platformTop + 5 && vy > 0) {\n            // Land on platform\n            y = platformTop - height;\n            vy = 0;\n            onGround = true;\n        }\n    }\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/game_development/physics_and_collisions/#collisions-not-detected","title":"Collisions Not Detected","text":"
    • Verify collision layers and masks overlap
    • Check that actors are added to the scene
    • Ensure Scene::update() is called
    • Verify hitboxes are correct
    "},{"location":"manual/game_development/physics_and_collisions/#physics-too-fastslow","title":"Physics Too Fast/Slow","text":"
    • Adjust values based on deltaTime
    • Check gravity and velocity values
    • Test on actual hardware (ESP32 may run slower)
    "},{"location":"manual/game_development/physics_and_collisions/#objects-passing-through","title":"Objects Passing Through","text":"
    • Use sweep tests for fast objects
    • Increase collision detection frequency
    • Check hitbox sizes match visual size
    "},{"location":"manual/game_development/physics_and_collisions/#next-steps","title":"Next Steps","text":"

    Now that you understand physics and collisions, learn about: - User Interface - Create menus and HUDs - Advanced Graphics - Advanced sprite techniques - Camera and Scrolling - Create scrolling levels

    See also: - API Reference - PhysicsActor - API Reference - CollisionSystem - Manual - Physics Overview - Manual - Collision Detection

    "},{"location":"manual/game_development/scenes_and_entities/","title":"Scenes and Entities","text":"

    Scenes and entities are the foundation of every PixelRoot32 game. This guide teaches you how to organize your game using scenes and create interactive game objects with entities.

    "},{"location":"manual/game_development/scenes_and_entities/#creating-a-scene","title":"Creating a Scene","text":"

    A Scene represents a screen or level in your game. To create a scene, inherit from pixelroot32::core::Scene and implement the three main methods:

    #include <core/Scene.h>\n#include <graphics/Renderer.h>\n\nclass MyGameScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Called once when the scene is initialized\n        // Set up your scene here: create entities, load resources, etc.\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Called every frame\n        // Update game logic here\n\n        // IMPORTANT: Always call parent update to update all entities\n        Scene::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Called every frame to draw\n        // Draw your scene here\n\n        // IMPORTANT: Always call parent draw to draw all entities\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/game_development/scenes_and_entities/#scene-lifecycle","title":"Scene Lifecycle","text":"
    1. init(): Called once when the scene is set as active
    2. Create and initialize entities
    3. Set up game state
    4. Load resources
    5. Configure palettes, audio, etc.

    6. update(deltaTime): Called every frame

    7. Process input
    8. Update game logic
    9. Handle collisions
    10. Must call Scene::update(deltaTime) to update all entities

    11. draw(renderer): Called every frame

    12. Draw background elements
    13. Draw UI elements
    14. Must call Scene::draw(renderer) to draw all entities
    "},{"location":"manual/game_development/scenes_and_entities/#basic-entities","title":"Basic Entities","text":"

    An Entity is any object in your game. To create an entity, inherit from pixelroot32::core::Entity:

    #include <core/Entity.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\nclass SimpleEntity : public pixelroot32::core::Entity {\npublic:\n    SimpleEntity(float x, float y)\n        : Entity(x, y, 16, 16, pixelroot32::core::EntityType::GENERIC) {\n        // Set render layer (0=background, 1=gameplay, 2=UI)\n        setRenderLayer(1);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Update entity logic\n        // For example, move the entity\n        this->x += 1.0f * (deltaTime * 0.001f); // Move right at 1 pixel per second\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw the entity\n        renderer.drawFilledRectangle(\n            static_cast<int>(x), \n            static_cast<int>(y), \n            width, \n            height, \n            pixelroot32::graphics::Color::Red\n        );\n    }\n};\n
    "},{"location":"manual/game_development/scenes_and_entities/#entity-properties","title":"Entity Properties","text":"

    Every entity has these properties:

    • x, y: Position in world space (float)
    • width, height: Dimensions (int)
    • isVisible: If false, draw() is not called
    • isEnabled: If false, update() is not called
    • renderLayer: Which layer to draw on (0, 1, or 2)
    "},{"location":"manual/game_development/scenes_and_entities/#adding-entities-to-a-scene","title":"Adding Entities to a Scene","text":"

    Add entities to your scene in init():

    void MyGameScene::init() override {\n    // Create entities\n    SimpleEntity* entity1 = new SimpleEntity(50, 50);\n    SimpleEntity* entity2 = new SimpleEntity(100, 100);\n\n    // Add them to the scene\n    addEntity(entity1);\n    addEntity(entity2);\n}\n

    The scene automatically manages these entities: - Calls update() on all enabled entities each frame - Calls draw() on all visible entities each frame - Handles cleanup when the scene is destroyed

    "},{"location":"manual/game_development/scenes_and_entities/#actors-entities-with-collisions","title":"Actors: Entities with Collisions","text":"

    An Actor is an entity that can participate in collision detection. Inherit from pixelroot32::core::Actor:

    #include <core/Actor.h>\n#include <physics/CollisionTypes.h>\n\nclass MyActor : public pixelroot32::core::Actor {\npublic:\n    MyActor(float x, float y, int w, int h)\n        : Actor(x, y, w, h) {\n        // Set collision layer (what group this actor belongs to)\n        setCollisionLayer(0x0001); // Example: layer 1\n\n        // Set collision mask (what groups this actor can collide with)\n        setCollisionMask(0x0002); // Example: can collide with layer 2\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Update actor logic\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw the actor\n    }\n\n    // REQUIRED: Define the hitbox for collision detection\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    // REQUIRED: Handle collisions\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // React to collision\n        // For example: take damage, destroy self, etc.\n    }\n};\n
    "},{"location":"manual/game_development/scenes_and_entities/#collision-layers-and-masks","title":"Collision Layers and Masks","text":"

    Collision layers use bit flags to organize actors into groups:

    // Define your layers (typically in a GameLayers.h file)\nnamespace Layers {\n    constexpr uint16_t PLAYER = 0x0001;  // Bit 0\n    constexpr uint16_t ENEMY = 0x0002;  // Bit 1\n    constexpr uint16_t PROJECTILE = 0x0004; // Bit 2\n    constexpr uint16_t WALL = 0x0008;   // Bit 3\n}\n\n// Set up a player actor\nplayer->setCollisionLayer(Layers::PLAYER);\nplayer->setCollisionMask(Layers::ENEMY | Layers::WALL); // Can collide with enemies and walls\n\n// Set up an enemy actor\nenemy->setCollisionLayer(Layers::ENEMY);\nenemy->setCollisionMask(Layers::PLAYER | Layers::PROJECTILE); // Can collide with player and projectiles\n

    The collision system only checks collisions between actors whose layers and masks overlap. This is much more efficient than checking every pair.

    "},{"location":"manual/game_development/scenes_and_entities/#scene-management","title":"Scene Management","text":""},{"location":"manual/game_development/scenes_and_entities/#setting-the-active-scene","title":"Setting the Active Scene","text":"

    From your main code, set the active scene:

    MyGameScene gameScene;\n\nvoid setup() {\n    engine.init();\n    gameScene.init();\n    engine.setScene(&gameScene); // Set as active scene\n}\n
    "},{"location":"manual/game_development/scenes_and_entities/#switching-scenes","title":"Switching Scenes","text":"

    To switch to a different scene:

    MenuScene menuScene;\nGameScene gameScene;\n\nvoid switchToGame() {\n    gameScene.init();\n    engine.setScene(&gameScene); // Replaces current scene\n}\n
    "},{"location":"manual/game_development/scenes_and_entities/#scene-stack-pushpop","title":"Scene Stack (Push/Pop)","text":"

    For menus and pause screens, use the scene stack:

    // Push a pause menu (game scene stays in background)\nvoid pauseGame() {\n    pauseMenu.init();\n    engine.getCurrentScene()->getSceneManager().pushScene(&pauseMenu);\n}\n\n// Pop the pause menu (resume game)\nvoid resumeGame() {\n    engine.getCurrentScene()->getSceneManager().popScene();\n}\n

    Note: Scene stack management is handled internally by the Engine's SceneManager. You typically access it through engine.getCurrentScene().

    "},{"location":"manual/game_development/scenes_and_entities/#complete-example","title":"Complete Example","text":"

    Here's a complete example of a scene with multiple entities:

    #include <core/Scene.h>\n#include <core/Entity.h>\n#include <core/Actor.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\n// A simple moving entity\nclass MovingBox : public pixelroot32::core::Entity {\npublic:\n    MovingBox(float x, float y) \n        : Entity(x, y, 20, 20, pixelroot32::core::EntityType::GENERIC),\n          speedX(50.0f), speedY(30.0f) {\n        setRenderLayer(1);\n    }\n\n    void update(unsigned long deltaTime) override {\n        float dt = deltaTime * 0.001f; // Convert to seconds\n\n        x += speedX * dt;\n        y += speedY * dt;\n\n        // Bounce off screen edges\n        if (x < 0 || x > 220) speedX = -speedX;\n        if (y < 0 || y > 220) speedY = -speedY;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x), \n            static_cast<int>(y), \n            width, \n            height, \n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n\nprivate:\n    float speedX, speedY;\n};\n\n// A simple actor that can collide\nclass CollidableBox : public pixelroot32::core::Actor {\npublic:\n    CollidableBox(float x, float y)\n        : Actor(x, y, 30, 30) {\n        setRenderLayer(1);\n        setCollisionLayer(0x0001);\n        setCollisionMask(0x0001);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Static actor, no movement\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x), \n            static_cast<int>(y), \n            width, \n            height, \n            pixelroot32::graphics::Color::Yellow\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Change color when collided\n        // (In a real game, you'd handle collision logic here)\n    }\n};\n\n// The scene\nclass ExampleScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Create and add entities\n        addEntity(new MovingBox(50, 50));\n        addEntity(new MovingBox(150, 100));\n        addEntity(new CollidableBox(100, 100));\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime); // Update all entities\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw background\n        renderer.drawFilledRectangle(0, 0, 240, 240, \n            pixelroot32::graphics::Color::Black);\n\n        Scene::draw(renderer); // Draw all entities\n    }\n};\n
    "},{"location":"manual/game_development/scenes_and_entities/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/scenes_and_entities/#entity-management","title":"Entity Management","text":"
    • Pre-allocate entities: Create entities in init(), not in update()
    • Reuse entities: Instead of creating/destroying, enable/disable entities
    • Limit entity count: MAX_ENTITIES = 32 per scene
    • Use object pooling: For frequently created/destroyed entities (projectiles, particles)
    "},{"location":"manual/game_development/scenes_and_entities/#scene-organization","title":"Scene Organization","text":"
    • One scene per screen: Menu, game, game over, etc.
    • Keep scenes focused: Each scene should have a single responsibility
    • Initialize in init(): Don't do heavy work in the constructor
    • Clean up properly: Remove entities when switching scenes
    "},{"location":"manual/game_development/scenes_and_entities/#collision-layers","title":"Collision Layers","text":"
    • Plan your layers: Design your layer system before coding
    • Use bit flags: Makes layer combinations easy
    • Keep it simple: Don't over-complicate with too many layers
    • Document your layers: Create a GameLayers.h file
    "},{"location":"manual/game_development/scenes_and_entities/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/scenes_and_entities/#entity-pool-pattern","title":"Entity Pool Pattern","text":"

    For entities that are frequently created and destroyed (like projectiles):

    class ProjectilePool {\n    static const int POOL_SIZE = 10;\n    ProjectileActor pool[POOL_SIZE];\n\npublic:\n    ProjectileActor* getAvailable() {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (!pool[i].isActive) {\n                pool[i].isActive = true;\n                return &pool[i];\n            }\n        }\n        return nullptr; // Pool exhausted\n    }\n};\n
    "},{"location":"manual/game_development/scenes_and_entities/#entity-factory-pattern","title":"Entity Factory Pattern","text":"

    Create entities through factory functions:

    Entity* createEnemy(EnemyType type, float x, float y) {\n    switch (type) {\n        case EnemyType::BASIC:\n            return new BasicEnemy(x, y);\n        case EnemyType::FAST:\n            return new FastEnemy(x, y);\n        // ...\n    }\n}\n
    "},{"location":"manual/game_development/scenes_and_entities/#next-steps","title":"Next Steps","text":"

    Now that you understand scenes and entities, learn about: - Basic Rendering - Draw sprites, text, and primitives - Input and Control - Handle user input - Physics and Collisions - Advanced collision handling

    See also: - Fundamental Concepts - API Reference - Scene - API Reference - Entity - API Reference - Actor

    "},{"location":"manual/game_development/user_interface/","title":"User Interface","text":"

    PixelRoot32 provides a complete UI system for creating menus, HUDs, and interface elements. This guide covers all UI components and layout systems.

    "},{"location":"manual/game_development/user_interface/#ui-elements","title":"UI Elements","text":"

    All UI elements inherit from UIElement, which itself inherits from Entity. This means UI elements can be added to scenes just like any other entity.

    "},{"location":"manual/game_development/user_interface/#uilabel","title":"UILabel","text":"

    Display text on screen:

    #include <graphics/ui/UILabel.h>\n\n// Create a label\npixelroot32::graphics::ui::UILabel* scoreLabel = new pixelroot32::graphics::ui::UILabel(\n    \"Score: 0\",                    // text\n    10,                            // x position\n    10,                            // y position\n    pixelroot32::graphics::Color::White,  // color\n    1                              // size multiplier\n);\n\n// Add to scene\naddEntity(scoreLabel);\n\n// Update text dynamically\nscoreLabel->setText(\"Score: 100\");\n\n// Center horizontally\nscoreLabel->centerX(240); // Screen width\n
    "},{"location":"manual/game_development/user_interface/#uibutton","title":"UIButton","text":"

    Create clickable buttons:

    #include <graphics/ui/UIButton.h>\n\n// Create a button\npixelroot32::graphics::ui::UIButton* startButton = new pixelroot32::graphics::ui::UIButton(\n    \"Start Game\",                  // text\n    4,                             // navigation index\n    50,                            // x position\n    100,                           // y position\n    140,                           // width\n    30,                            // height\n    []() {                         // callback function\n        // Button clicked - start game\n        startGame();\n    }\n);\n\n// Configure style\nstartButton->setStyle(\n    pixelroot32::graphics::Color::White,   // text color\n    pixelroot32::graphics::Color::Blue,    // background color\n    true                                    // draw background\n);\n\n// Add to scene\naddEntity(startButton);\n
    "},{"location":"manual/game_development/user_interface/#uicheckbox","title":"UICheckBox","text":"

    Create interactive checkboxes:

    #include <graphics/ui/UICheckBox.h>\n\n// Create a checkbox\npixelroot32::graphics::ui::UICheckBox* soundCheckbox = new pixelroot32::graphics::ui::UICheckBox(\n    \"Enable Sound\",                // text\n    4,                             // navigation index\n    50,                            // x position\n    140,                           // y position\n    140,                           // width\n    20,                            // height\n    true,                          // initial checked state\n    [](bool checked) {             // callback function\n        // Checkbox state changed\n        setSoundEnabled(checked);\n    },\n    1                              // font size\n);\n\n// Configure style\nsoundCheckbox->setStyle(\n    pixelroot32::graphics::Color::White,   // text color\n    pixelroot32::graphics::Color::Blue,    // background color\n    false                                  // draw background\n);\n\n// Add to scene\naddEntity(soundCheckbox);\n
    "},{"location":"manual/game_development/user_interface/#uipanel","title":"UIPanel","text":"

    Create visual containers with background and border:

    #include <graphics/ui/UIPanel.h>\n\n// Create a panel\npixelroot32::graphics::ui::UIPanel* dialog = new pixelroot32::graphics::ui::UIPanel(\n    50,   // x\n    50,   // y\n    140,  // width\n    140   // height\n);\n\n// Configure appearance\ndialog->setBackgroundColor(pixelroot32::graphics::Color::Black);\ndialog->setBorderColor(pixelroot32::graphics::Color::White);\ndialog->setBorderWidth(2);\n\n// Add content (typically a layout)\ndialog->setChild(menuLayout);\n\n// Add to scene\naddEntity(dialog);\n
    "},{"location":"manual/game_development/user_interface/#layouts","title":"Layouts","text":"

    Layouts automatically organize UI elements, eliminating the need for manual position calculations.

    "},{"location":"manual/game_development/user_interface/#uiverticallayout","title":"UIVerticalLayout","text":"

    Organize elements vertically with automatic scrolling:

    #include <graphics/ui/UIVerticalLayout.h>\n\n// Create vertical layout\npixelroot32::graphics::ui::UIVerticalLayout* menu = new pixelroot32::graphics::ui::UIVerticalLayout(\n    10,   // x\n    60,   // y\n    220,  // width\n    160   // height (viewport)\n);\n\n// Configure layout\nmenu->setPadding(5);        // Internal padding\nmenu->setSpacing(6);        // Space between elements\nmenu->setScrollEnabled(true); // Enable scrolling\n\n// Set navigation buttons\nmenu->setNavigationButtons(0, 1); // UP=0, DOWN=1\n\n// Set button styles\nmenu->setButtonStyle(\n    pixelroot32::graphics::Color::White,  // selected text\n    pixelroot32::graphics::Color::Cyan,    // selected background\n    pixelroot32::graphics::Color::White,  // unselected text\n    pixelroot32::graphics::Color::Black   // unselected background\n);\n\n// Add buttons (no manual positioning needed!)\nfor (int i = 0; i < 10; i++) {\n    UIButton* btn = new UIButton(\n        \"Option \" + std::to_string(i),\n        i,\n        0, 0,  // Position ignored - layout handles it\n        200, 20,\n        [i]() { handleOption(i); }\n    );\n    menu->addElement(btn);\n}\n\n// Add layout to scene\nmenu->setRenderLayer(2); // UI layer\naddEntity(menu);\n
    "},{"location":"manual/game_development/user_interface/#uihorizontallayout","title":"UIHorizontalLayout","text":"

    Organize elements horizontally:

    #include <graphics/ui/UIHorizontalLayout.h>\n\n// Create horizontal layout (menu bar)\npixelroot32::graphics::ui::UIHorizontalLayout* menuBar = new pixelroot32::graphics::ui::UIHorizontalLayout(\n    0,    // x\n    0,    // y\n    240,  // width\n    30    // height\n);\n\nmenuBar->setPadding(5);\nmenuBar->setSpacing(4);\nmenuBar->setScrollEnabled(true);\nmenuBar->setNavigationButtons(2, 3); // LEFT=2, RIGHT=3\n\n// Add menu items\nmenuBar->addElement(new UIButton(\"File\", 0, 0, 0, 60, 20, []() {}));\nmenuBar->addElement(new UIButton(\"Edit\", 1, 0, 0, 60, 20, []() {}));\nmenuBar->addElement(new UIButton(\"View\", 2, 0, 0, 60, 20, []() {}));\n\naddEntity(menuBar);\n
    "},{"location":"manual/game_development/user_interface/#uigridlayout","title":"UIGridLayout","text":"

    Organize elements in a grid (matrix):

    #include <graphics/ui/UIGridLayout.h>\n\n// Create grid layout (inventory)\npixelroot32::graphics::ui::UIGridLayout* inventory = new pixelroot32::graphics::ui::UIGridLayout(\n    10,   // x\n    60,   // y\n    220,  // width\n    160   // height\n);\n\ninventory->setColumns(4);  // 4 columns\ninventory->setPadding(5);\ninventory->setSpacing(4);\ninventory->setNavigationButtons(0, 1, 2, 3); // UP, DOWN, LEFT, RIGHT\n\n// Add items (automatically arranged in grid)\nfor (int i = 0; i < 16; i++) {\n    UIButton* item = new UIButton(\n        \"Item \" + std::to_string(i),\n        i,\n        0, 0,  // Position ignored\n        50, 50,\n        [i]() { useItem(i); }\n    );\n    inventory->addElement(item);\n}\n\naddEntity(inventory);\n
    "},{"location":"manual/game_development/user_interface/#uianchorlayout","title":"UIAnchorLayout","text":"

    Position elements at fixed screen positions (perfect for HUDs):

    #include <graphics/ui/UIAnchorLayout.h>\n\n// Create anchor layout for HUD\npixelroot32::graphics::ui::UIAnchorLayout* hud = new pixelroot32::graphics::ui::UIAnchorLayout(\n    0,    // x\n    0,    // y\n    240,  // screen width\n    240   // screen height\n);\n\nhud->setScreenSize(240, 240);\n\n// Add HUD elements at different anchor points\nUILabel* scoreLabel = new UILabel(\"Score: 0\", 0, 0, Color::White, 1);\nUILabel* livesLabel = new UILabel(\"Lives: 3\", 0, 0, Color::White, 1);\n\nhud->addElement(scoreLabel, pixelroot32::graphics::ui::Anchor::TOP_LEFT);\nhud->addElement(livesLabel, pixelroot32::graphics::ui::Anchor::TOP_RIGHT);\n\naddEntity(hud);\n

    Available Anchors: - TOP_LEFT, TOP_RIGHT, TOP_CENTER - BOTTOM_LEFT, BOTTOM_RIGHT, BOTTOM_CENTER - LEFT_CENTER, RIGHT_CENTER - CENTER

    "},{"location":"manual/game_development/user_interface/#uipaddingcontainer","title":"UIPaddingContainer","text":"

    Add padding around a single element:

    #include <graphics/ui/UIPaddingContainer.h>\n\n// Create padding container\npixelroot32::graphics::ui::UIPaddingContainer* container = new pixelroot32::graphics::ui::UIPaddingContainer(\n    10,   // x\n    10,   // y\n    200,  // width\n    100   // height\n);\n\n// Set uniform padding\ncontainer->setPadding(10);\n\n// Or set asymmetric padding\ncontainer->setPadding(5, 15, 10, 10); // left, right, top, bottom\n\n// Add child element\ncontainer->setChild(button);\n\naddEntity(container);\n
    "},{"location":"manual/game_development/user_interface/#navigation","title":"Navigation","text":"

    Layouts handle D-pad navigation automatically:

    "},{"location":"manual/game_development/user_interface/#vertical-navigation","title":"Vertical Navigation","text":"
    verticalLayout->setNavigationButtons(Buttons::UP, Buttons::DOWN);\n\n// Layout automatically:\n// - Highlights selected button\n// - Scrolls to keep selected button visible\n// - Handles wrapping (optional)\n
    "},{"location":"manual/game_development/user_interface/#horizontal-navigation","title":"Horizontal Navigation","text":"
    horizontalLayout->setNavigationButtons(Buttons::LEFT, Buttons::RIGHT);\n
    "},{"location":"manual/game_development/user_interface/#grid-navigation","title":"Grid Navigation","text":"
    gridLayout->setNavigationButtons(Buttons::UP, Buttons::DOWN, Buttons::LEFT, Buttons::RIGHT);\n\n// Layout automatically:\n// - Handles 4-direction navigation\n// - Wraps around edges\n// - Updates selection\n
    "},{"location":"manual/game_development/user_interface/#manual-selection","title":"Manual Selection","text":"
    // Set selected element programmatically\nlayout->setSelectedIndex(2);\n\n// Get selected element\nint selected = layout->getSelectedIndex();\nUIElement* element = layout->getSelectedElement();\n
    "},{"location":"manual/game_development/user_interface/#complete-example-main-menu","title":"Complete Example: Main Menu","text":"
    #include <core/Scene.h>\n#include <graphics/ui/UIVerticalLayout.h>\n#include <graphics/ui/UIButton.h>\n#include <graphics/ui/UILabel.h>\n#include <graphics/ui/UIPanel.h>\n\nclass MainMenuScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIVerticalLayout* menuLayout;\n    pixelroot32::graphics::ui::UIPanel* menuPanel;\n\npublic:\n    void init() override {\n        // Create panel\n        menuPanel = new pixelroot32::graphics::ui::UIPanel(40, 40, 160, 160);\n        menuPanel->setBackgroundColor(pixelroot32::graphics::Color::Black);\n        menuPanel->setBorderColor(pixelroot32::graphics::Color::White);\n        menuPanel->setBorderWidth(2);\n        menuPanel->setRenderLayer(2);\n        addEntity(menuPanel);\n\n        // Create layout inside panel\n        menuLayout = new pixelroot32::graphics::ui::UIVerticalLayout(0, 0, 160, 160);\n        menuLayout->setPadding(10);\n        menuLayout->setSpacing(8);\n        menuLayout->setNavigationButtons(0, 1);\n        menuLayout->setButtonStyle(\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Cyan,\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Black\n        );\n\n        // Add title\n        pixelroot32::graphics::ui::UILabel* title = new pixelroot32::graphics::ui::UILabel(\n            \"GAME MENU\", 0, 0, pixelroot32::graphics::Color::Yellow, 2\n        );\n        menuLayout->addElement(title);\n\n        // Add menu buttons\n        menuLayout->addElement(new pixelroot32::graphics::ui::UIButton(\n            \"Start Game\", 0, 0, 0, 140, 25, []() { startGame(); }\n        ));\n\n        menuLayout->addElement(new pixelroot32::graphics::ui::UIButton(\n            \"Options\", 1, 0, 0, 140, 25, []() { showOptions(); }\n        ));\n\n        menuLayout->addElement(new pixelroot32::graphics::ui::UIButton(\n            \"Quit\", 2, 0, 0, 140, 25, []() { quitGame(); }\n        ));\n\n        menuPanel->setChild(menuLayout);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Handle input for layout navigation\n        auto& input = engine.getInputManager();\n        menuLayout->handleInput(input);\n\n        Scene::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/game_development/user_interface/#complete-example-hud","title":"Complete Example: HUD","text":"
    class GameHUD : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::ui::UIAnchorLayout* hud;\n    pixelroot32::graphics::ui::UILabel* scoreLabel;\n    pixelroot32::graphics::ui::UILabel* livesLabel;\n    pixelroot32::graphics::ui::UILabel* healthLabel;\n\npublic:\n    GameHUD()\n        : Entity(0, 0, 240, 240, pixelroot32::core::EntityType::UI_ELEMENT) {\n        setRenderLayer(2); // UI layer\n\n        // Create HUD layout\n        hud = new pixelroot32::graphics::ui::UIAnchorLayout(0, 0, 240, 240);\n        hud->setScreenSize(240, 240);\n\n        // Create labels\n        scoreLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Score: 0\", 0, 0, pixelroot32::graphics::Color::White, 1\n        );\n\n        livesLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Lives: 3\", 0, 0, pixelroot32::graphics::Color::White, 1\n        );\n\n        healthLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Health: 100%\", 0, 0, pixelroot32::graphics::Color::Green, 1\n        );\n\n        // Position labels\n        hud->addElement(scoreLabel, pixelroot32::graphics::ui::Anchor::TOP_LEFT);\n        hud->addElement(livesLabel, pixelroot32::graphics::ui::Anchor::TOP_RIGHT);\n        hud->addElement(healthLabel, pixelroot32::graphics::ui::Anchor::BOTTOM_CENTER);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Update HUD text (example)\n        char buffer[32];\n        snprintf(buffer, sizeof(buffer), \"Score: %d\", currentScore);\n        scoreLabel->setText(buffer);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // HUD draws itself through its layout\n    }\n\n    // Add HUD to scene\n    void addToScene(Scene* scene) {\n        scene->addEntity(hud);\n    }\n};\n
    "},{"location":"manual/game_development/user_interface/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/user_interface/#organization","title":"Organization","text":"
    • Use render layer 2: Keep all UI on the top layer
    • Group related elements: Use panels to group menu items
    • Separate HUD from menus: Use different layouts for different UI types
    • Reuse layouts: Create layout factories for common patterns
    "},{"location":"manual/game_development/user_interface/#performance","title":"Performance","text":"
    • Layouts use viewport culling: Only visible elements are rendered
    • Minimize text updates: Updating text has overhead
    • Use appropriate layouts: Choose the right layout for your needs
    • Limit element count: Too many elements can impact performance
    "},{"location":"manual/game_development/user_interface/#navigation_1","title":"Navigation","text":"
    • Set navigation buttons: Configure D-pad navigation for layouts
    • Handle input in update(): Check for button presses to trigger actions
    • Provide visual feedback: Selected buttons should be clearly visible
    • Test navigation flow: Ensure navigation feels responsive
    "},{"location":"manual/game_development/user_interface/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/user_interface/#menu-system","title":"Menu System","text":"
    class MenuSystem {\n    UIVerticalLayout* currentMenu;\n\npublic:\n    void showMainMenu() {\n        currentMenu = createMainMenu();\n        scene->addEntity(currentMenu);\n    }\n\n    void showPauseMenu() {\n        currentMenu = createPauseMenu();\n        scene->addEntity(currentMenu);\n    }\n\n    void hideMenu() {\n        if (currentMenu) {\n            scene->removeEntity(currentMenu);\n            currentMenu = nullptr;\n        }\n    }\n};\n
    "},{"location":"manual/game_development/user_interface/#dynamic-ui-updates","title":"Dynamic UI Updates","text":"
    void updateHUD(int score, int lives, int health) {\n    char buffer[32];\n\n    snprintf(buffer, sizeof(buffer), \"Score: %d\", score);\n    scoreLabel->setText(buffer);\n\n    snprintf(buffer, sizeof(buffer), \"Lives: %d\", lives);\n    livesLabel->setText(buffer);\n\n    snprintf(buffer, sizeof(buffer), \"Health: %d%%\", health);\n    healthLabel->setText(buffer);\n}\n
    "},{"location":"manual/game_development/user_interface/#next-steps","title":"Next Steps","text":"

    Now that you understand the UI system, you've completed the core game development topics. Continue with: - Advanced Graphics - Advanced sprite techniques - Camera and Scrolling - Create scrolling levels - Performance Tuning - Improve performance

    See also: - API Reference - UIElement - API Reference - UIButton - API Reference - UI Layouts - Manual - UI Overview

    "},{"location":"manual/optimization/extensibility/","title":"Extensibility","text":"

    PixelRoot32 is designed to be extensible. This guide covers how to create custom drivers, audio backends, and extend existing systems.

    "},{"location":"manual/optimization/extensibility/#creating-custom-display-drivers","title":"Creating Custom Display Drivers","text":"

    To support a new display, implement the DrawSurface interface.

    "},{"location":"manual/optimization/extensibility/#drawsurface-interface","title":"DrawSurface Interface","text":"
    #include <graphics/DrawSurface.h>\n\nclass MyCustomDrawer : public pixelroot32::graphics::DrawSurface {\npublic:\n    // Required methods\n    void init() override;\n    void setRotation(uint8_t rotation) override;\n    void clearBuffer() override;\n    void sendBuffer() override;\n\n    // Drawing primitives\n    void drawPixel(int x, int y, uint16_t color) override;\n    void drawLine(int x1, int y1, int x2, int y2, uint16_t color) override;\n    void drawRectangle(int x, int y, int width, int height, uint16_t color) override;\n    void drawFilledRectangle(int x, int y, int width, int height, uint16_t color) override;\n    void drawCircle(int x, int y, int radius, uint16_t color) override;\n    void drawFilledCircle(int x, int y, int radius, uint16_t color) override;\n    void drawBitmap(int x, int y, int width, int height, const uint8_t* bitmap, uint16_t color) override;\n\n    // Text (deprecated, but must implement)\n    void drawText(const char* text, int16_t x, int16_t y, uint16_t color, uint8_t size) override;\n    void drawTextCentered(const char* text, int16_t y, uint16_t color, uint8_t size) override;\n\n    // State management\n    void setTextColor(uint16_t color) override;\n    void setTextSize(uint8_t size) override;\n    void setCursor(int16_t x, int16_t y) override;\n    void setContrast(uint8_t level) override;\n    void setDisplaySize(int w, int h) override;\n\n    // Utilities\n    uint16_t color565(uint8_t r, uint8_t g, uint8_t b) override;\n    bool processEvents() override;\n    void present() override;\n};\n
    "},{"location":"manual/optimization/extensibility/#example-simple-custom-drawer","title":"Example: Simple Custom Drawer","text":"
    #include <graphics/DrawSurface.h>\n\nclass SimpleDrawer : public pixelroot32::graphics::DrawSurface {\nprivate:\n    uint16_t* framebuffer;\n    int width, height;\n\npublic:\n    SimpleDrawer(int w, int h) : width(w), height(h) {\n        framebuffer = new uint16_t[w * h];\n    }\n\n    ~SimpleDrawer() {\n        delete[] framebuffer;\n    }\n\n    void init() override {\n        // Initialize your display hardware\n        // Clear framebuffer\n        clearBuffer();\n    }\n\n    void clearBuffer() override {\n        for (int i = 0; i < width * height; i++) {\n            framebuffer[i] = 0x0000; // Black\n        }\n    }\n\n    void sendBuffer() override {\n        // Send framebuffer to display\n        // Implementation depends on your hardware\n    }\n\n    void drawPixel(int x, int y, uint16_t color) override {\n        if (x >= 0 && x < width && y >= 0 && y < height) {\n            framebuffer[y * width + x] = color;\n        }\n    }\n\n    void drawFilledRectangle(int x, int y, int w, int h, uint16_t color) override {\n        for (int py = y; py < y + h; py++) {\n            for (int px = x; px < x + w; px++) {\n                drawPixel(px, py, color);\n            }\n        }\n    }\n\n    // Implement other required methods...\n    // (See TFT_eSPI_Drawer or SDL2_Drawer for reference implementations)\n};\n
    "},{"location":"manual/optimization/extensibility/#integrating-custom-driver","title":"Integrating Custom Driver","text":"
    // Create custom drawer\nSimpleDrawer* customDrawer = new SimpleDrawer(240, 240);\n\n// Create renderer with custom drawer\npixelroot32::graphics::DisplayConfig config(\n    pixelroot32::graphics::DisplayType::NONE, // Use NONE for custom\n    0, 240, 240\n);\n\n// You'll need to modify Engine to accept custom DrawSurface\n// Or create a custom Engine wrapper\n
    "},{"location":"manual/optimization/extensibility/#creating-custom-audio-backends","title":"Creating Custom Audio Backends","text":"

    Implement the AudioBackend interface for custom audio hardware.

    "},{"location":"manual/optimization/extensibility/#audiobackend-interface","title":"AudioBackend Interface","text":"
    #include <audio/AudioBackend.h>\n\nclass MyCustomAudioBackend : public pixelroot32::audio::AudioBackend {\npublic:\n    // Required methods\n    void init() override;\n    void start() override;\n    void stop() override;\n    uint32_t getSampleRate() const override;\n\n    // Audio generation\n    int16_t generateSample() override;\n\n    // Channel management\n    void setChannelWave(int channel, pixelroot32::audio::WaveType type, float frequency, float duty) override;\n    void setChannelVolume(int channel, float volume) override;\n    void stopChannel(int channel) override;\n};\n
    "},{"location":"manual/optimization/extensibility/#example-custom-audio-backend","title":"Example: Custom Audio Backend","text":"
    #include <audio/AudioBackend.h>\n\nclass CustomAudioBackend : public pixelroot32::audio::AudioBackend {\nprivate:\n    uint32_t sampleRate;\n    float phase[4] = {0, 0, 0, 0}; // 4 channels\n    float frequency[4] = {0, 0, 0, 0};\n    float volume[4] = {0, 0, 0, 0};\n    pixelroot32::audio::WaveType waveType[4];\n\npublic:\n    CustomAudioBackend(uint32_t rate) : sampleRate(rate) {\n        for (int i = 0; i < 4; i++) {\n            waveType[i] = pixelroot32::audio::WaveType::PULSE;\n            volume[i] = 0.0f;\n        }\n    }\n\n    void init() override {\n        // Initialize your audio hardware\n    }\n\n    void start() override {\n        // Start audio output\n    }\n\n    void stop() override {\n        // Stop audio output\n    }\n\n    uint32_t getSampleRate() const override {\n        return sampleRate;\n    }\n\n    int16_t generateSample() override {\n        float sample = 0.0f;\n\n        for (int ch = 0; ch < 4; ch++) {\n            if (frequency[ch] > 0 && volume[ch] > 0) {\n                float phaseIncrement = frequency[ch] / sampleRate;\n                phase[ch] += phaseIncrement;\n                if (phase[ch] >= 1.0f) phase[ch] -= 1.0f;\n\n                float channelSample = 0.0f;\n                switch (waveType[ch]) {\n                    case pixelroot32::audio::WaveType::PULSE:\n                        channelSample = (phase[ch] < 0.5f) ? 1.0f : -1.0f;\n                        break;\n                    case pixelroot32::audio::WaveType::TRIANGLE:\n                        channelSample = (phase[ch] < 0.5f) ? \n                            (phase[ch] * 4.0f - 1.0f) : \n                            (3.0f - phase[ch] * 4.0f);\n                        break;\n                    // ... other wave types\n                }\n\n                sample += channelSample * volume[ch];\n            }\n        }\n\n        // Clamp and convert to int16_t\n        if (sample > 1.0f) sample = 1.0f;\n        if (sample < -1.0f) sample = -1.0f;\n        return static_cast<int16_t>(sample * 32767.0f);\n    }\n\n    void setChannelWave(int ch, pixelroot32::audio::WaveType type, \n                       float freq, float duty) override {\n        if (ch >= 0 && ch < 4) {\n            waveType[ch] = type;\n            frequency[ch] = freq;\n        }\n    }\n\n    void setChannelVolume(int ch, float vol) override {\n        if (ch >= 0 && ch < 4) {\n            volume[ch] = vol;\n        }\n    }\n\n    void stopChannel(int ch) override {\n        if (ch >= 0 && ch < 4) {\n            frequency[ch] = 0.0f;\n            volume[ch] = 0.0f;\n        }\n    }\n};\n
    "},{"location":"manual/optimization/extensibility/#extending-existing-systems","title":"Extending Existing Systems","text":""},{"location":"manual/optimization/extensibility/#custom-entity-types","title":"Custom Entity Types","text":"

    Create specialized entity types:

    class PowerUpActor : public pixelroot32::core::Actor {\nprivate:\n    PowerUpType type;\n    float lifetime = 5.0f; // 5 seconds\n\npublic:\n    PowerUpActor(float x, float y, PowerUpType t)\n        : Actor(x, y, 8, 8), type(t) {\n        setRenderLayer(1);\n        setCollisionLayer(Layers::POWERUP);\n        setCollisionMask(Layers::PLAYER);\n    }\n\n    void update(unsigned long deltaTime) override {\n        lifetime -= deltaTime * 0.001f;\n        if (lifetime <= 0) {\n            isEnabled = false;\n            isVisible = false;\n        }\n\n        // Animate (bob up and down)\n        y += sin(millis() * 0.005f) * 0.5f;\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        if (other->isInLayer(Layers::PLAYER)) {\n            applyPowerUp(other);\n            isEnabled = false;\n            isVisible = false;\n        }\n    }\n\nprivate:\n    void applyPowerUp(pixelroot32::core::Actor* player) {\n        switch (type) {\n            case PowerUpType::SPEED:\n                // Increase player speed\n                break;\n            case PowerUpType::HEALTH:\n                // Restore health\n                break;\n            // ...\n        }\n    }\n};\n
    "},{"location":"manual/optimization/extensibility/#custom-ui-layouts","title":"Custom UI Layouts","text":"

    Create new layout types:

    #include <graphics/ui/UILayout.h>\n\nclass UICircularLayout : public pixelroot32::graphics::ui::UILayout {\nprivate:\n    float radius;\n    float startAngle;\n\npublic:\n    UICircularLayout(float x, float y, float w, float h, float r)\n        : UILayout(x, y, w, h), radius(r), startAngle(0.0f) {\n    }\n\n    void updateLayout() override {\n        int count = elements.size();\n        float angleStep = 360.0f / count;\n\n        for (size_t i = 0; i < elements.size(); i++) {\n            float angle = startAngle + (i * angleStep);\n            float rad = angle * M_PI / 180.0f;\n\n            float elementX = x + (radius * cos(rad)) - (elements[i]->width / 2);\n            float elementY = y + (radius * sin(rad)) - (elements[i]->height / 2);\n\n            elements[i]->x = elementX;\n            elements[i]->y = elementY;\n        }\n    }\n\n    void handleInput(const pixelroot32::input::InputManager& input) override {\n        // Implement circular navigation\n        // ...\n    }\n};\n
    "},{"location":"manual/optimization/extensibility/#custom-collision-primitives","title":"Custom Collision Primitives","text":"

    Extend collision system with new shapes:

    // Add to your game code (not engine modification)\nstruct Triangle {\n    float x1, y1, x2, y2, x3, y3;\n};\n\nbool intersects(const Triangle& tri, const pixelroot32::core::Rect& rect) {\n    // Implement triangle-rectangle intersection\n    // ...\n    return false;\n}\n\nbool intersects(const Triangle& tri1, const Triangle& tri2) {\n    // Implement triangle-triangle intersection\n    // ...\n    return false;\n}\n
    "},{"location":"manual/optimization/extensibility/#best-practices","title":"Best Practices","text":""},{"location":"manual/optimization/extensibility/#maintain-compatibility","title":"Maintain Compatibility","text":"
    • Don't break existing APIs: Extend, don't modify
    • Use inheritance: Inherit from base classes
    • Follow patterns: Match existing code patterns
    • Document extensions: Comment your custom code
    "},{"location":"manual/optimization/extensibility/#testing","title":"Testing","text":"
    • Test on both platforms: ESP32 and Native
    • Test edge cases: Boundary conditions, null pointers
    • Performance testing: Ensure extensions don't hurt performance
    • Memory testing: Check for leaks with custom code
    "},{"location":"manual/optimization/extensibility/#documentation","title":"Documentation","text":"
    • Comment your code: Explain why, not just what
    • Provide examples: Show how to use your extensions
    • Document limitations: State what doesn't work
    • Version compatibility: Note which engine version
    "},{"location":"manual/optimization/extensibility/#common-extension-patterns","title":"Common Extension Patterns","text":""},{"location":"manual/optimization/extensibility/#factory-pattern","title":"Factory Pattern","text":"
    class EntityFactory {\npublic:\n    static pixelroot32::core::Entity* createEnemy(EnemyType type, float x, float y) {\n        switch (type) {\n            case EnemyType::BASIC:\n                return new BasicEnemy(x, y);\n            case EnemyType::FAST:\n                return new FastEnemy(x, y);\n            case EnemyType::TANK:\n                return new TankEnemy(x, y);\n            default:\n                return nullptr;\n        }\n    }\n\n    static pixelroot32::core::Entity* createPowerUp(PowerUpType type, float x, float y) {\n        return new PowerUpActor(x, y, type);\n    }\n};\n
    "},{"location":"manual/optimization/extensibility/#strategy-pattern","title":"Strategy Pattern","text":"
    class MovementStrategy {\npublic:\n    virtual void update(pixelroot32::core::Actor* actor, unsigned long deltaTime) = 0;\n};\n\nclass LinearMovement : public MovementStrategy {\npublic:\n    void update(pixelroot32::core::Actor* actor, unsigned long deltaTime) override {\n        actor->x += speed * (deltaTime * 0.001f);\n    }\n};\n\nclass CircularMovement : public MovementStrategy {\npublic:\n    void update(pixelroot32::core::Actor* actor, unsigned long deltaTime) override {\n        float angle = millis() * 0.001f;\n        actor->x = centerX + radius * cos(angle);\n        actor->y = centerY + radius * sin(angle);\n    }\n};\n\nclass SmartEnemy : public pixelroot32::core::Actor {\nprivate:\n    MovementStrategy* movement;\n\npublic:\n    void setMovement(MovementStrategy* strat) {\n        movement = strat;\n    }\n\n    void update(unsigned long deltaTime) override {\n        if (movement) {\n            movement->update(this, deltaTime);\n        }\n    }\n};\n
    "},{"location":"manual/optimization/extensibility/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/optimization/extensibility/#driver-not-working","title":"Driver Not Working","text":"
    • Verify all interface methods are implemented
    • Check initialization order
    • Test with simple drawing first
    • Verify hardware connections
    "},{"location":"manual/optimization/extensibility/#audio-backend-issues","title":"Audio Backend Issues","text":"
    • Check sample rate matches hardware
    • Verify audio generation logic
    • Test with simple tones first
    • Check channel management
    "},{"location":"manual/optimization/extensibility/#extension-conflicts","title":"Extension Conflicts","text":"
    • Ensure namespace isolation
    • Avoid modifying engine code directly
    • Use composition over modification
    • Test with engine updates
    "},{"location":"manual/optimization/extensibility/#next-steps","title":"Next Steps","text":"

    Now that you understand extensibility, you've completed the optimization section. Continue with: - API Reference - Complete API documentation - Examples - Code examples - Resources - Tools and troubleshooting

    See also: - API Reference - DrawSurface - API Reference - AudioBackend - Manual - Platforms and Drivers

    "},{"location":"manual/optimization/memory_management/","title":"Memory Management","text":"

    ESP32 has limited memory, so efficient memory management is crucial for PixelRoot32 games. This guide covers memory constraints, object pooling, and best practices.

    "},{"location":"manual/optimization/memory_management/#esp32-memory-constraints","title":"ESP32 Memory Constraints","text":""},{"location":"manual/optimization/memory_management/#available-memory","title":"Available Memory","text":"

    ESP32 typically has: - RAM: ~320KB total (varies by model) - Flash: 4MB+ (for program storage) - Heap: Limited and fragmented over time

    "},{"location":"manual/optimization/memory_management/#real-world-limits","title":"Real-World Limits","text":"
    • MAX_ENTITIES: 32 per scene (hard limit)
    • Sprite data: Stored in flash (const/constexpr)
    • Dynamic allocation: Should be avoided in game loop
    • Stack: Limited (~8KB), avoid large stack allocations
    "},{"location":"manual/optimization/memory_management/#object-pooling","title":"Object Pooling","text":"

    Object pooling reuses objects instead of creating/destroying them, avoiding memory fragmentation.

    "},{"location":"manual/optimization/memory_management/#basic-pool-pattern","title":"Basic Pool Pattern","text":"
    class ProjectilePool {\nprivate:\n    static const int POOL_SIZE = 10;\n    ProjectileActor pool[POOL_SIZE];\n    bool inUse[POOL_SIZE];\n\npublic:\n    ProjectilePool() {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            inUse[i] = false;\n        }\n    }\n\n    ProjectileActor* getAvailable() {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (!inUse[i]) {\n                inUse[i] = true;\n                return &pool[i];\n            }\n        }\n        return nullptr; // Pool exhausted\n    }\n\n    void release(ProjectileActor* projectile) {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (&pool[i] == projectile) {\n                inUse[i] = false;\n                projectile->isEnabled = false;\n                projectile->isVisible = false;\n                break;\n            }\n        }\n    }\n};\n
    "},{"location":"manual/optimization/memory_management/#using-object-pools","title":"Using Object Pools","text":"
    class GameScene : public pixelroot32::core::Scene {\nprivate:\n    ProjectilePool projectilePool;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Fire projectile\n        if (input.isButtonPressed(Buttons::A)) {\n            ProjectileActor* proj = projectilePool.getAvailable();\n            if (proj) {\n                proj->x = player->x;\n                proj->y = player->y;\n                proj->isEnabled = true;\n                proj->isVisible = true;\n                // ... initialize projectile\n            }\n        }\n\n        // Clean up projectiles that hit target\n        for (auto* entity : entities) {\n            if (auto* proj = dynamic_cast<ProjectileActor*>(entity)) {\n                if (proj->hitTarget) {\n                    projectilePool.release(proj);\n                }\n            }\n        }\n    }\n};\n
    "},{"location":"manual/optimization/memory_management/#complete-example-entity-pool","title":"Complete Example: Entity Pool","text":"
    template<typename T, int POOL_SIZE>\nclass EntityPool {\nprivate:\n    T pool[POOL_SIZE];\n    bool inUse[POOL_SIZE];\n    int activeCount = 0;\n\npublic:\n    EntityPool() {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            inUse[i] = false;\n        }\n    }\n\n    T* acquire() {\n        if (activeCount >= POOL_SIZE) {\n            return nullptr; // Pool full\n        }\n\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (!inUse[i]) {\n                inUse[i] = true;\n                activeCount++;\n                return &pool[i];\n            }\n        }\n        return nullptr;\n    }\n\n    void release(T* obj) {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (&pool[i] == obj) {\n                inUse[i] = false;\n                activeCount--;\n                obj->isEnabled = false;\n                obj->isVisible = false;\n                break;\n            }\n        }\n    }\n\n    int getActiveCount() const { return activeCount; }\n    int getAvailableCount() const { return POOL_SIZE - activeCount; }\n};\n\n// Usage\nEntityPool<EnemyActor, 8> enemyPool;\nEntityPool<ParticleEmitter, 5> particlePool;\n
    "},{"location":"manual/optimization/memory_management/#scene-arena-experimental","title":"Scene Arena (Experimental)","text":"

    Scene Arena provides a memory arena for scene-specific allocations, reducing fragmentation.

    "},{"location":"manual/optimization/memory_management/#what-is-scene-arena","title":"What is Scene Arena?","text":"

    Scene Arena is a contiguous memory block pre-allocated for a scene. All scene entities are allocated from this arena instead of the heap.

    "},{"location":"manual/optimization/memory_management/#when-to-use","title":"When to Use","text":"
    • Large scenes: Scenes with many entities
    • Frequent allocation: Scenes that create/destroy entities often
    • Memory fragmentation: When heap fragmentation is a problem
    • Performance: When you need predictable allocation performance
    "},{"location":"manual/optimization/memory_management/#configuration","title":"Configuration","text":"
    #ifdef PIXELROOT32_ENABLE_SCENE_ARENA\n#include <core/Scene.h>\n\n// Define arena buffer (typically in scene header)\nstatic unsigned char MY_SCENE_ARENA_BUFFER[8192]; // 8KB arena\n\nclass MyScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Initialize arena\n        arena.init(MY_SCENE_ARENA_BUFFER, sizeof(MY_SCENE_ARENA_BUFFER));\n\n        // Now entities allocated with arena will use this memory\n        // (Requires custom allocation functions)\n    }\n};\n#endif\n
    "},{"location":"manual/optimization/memory_management/#limitations","title":"Limitations","text":"
    • Experimental: May have bugs or limitations
    • Fixed size: Arena size must be determined at compile time
    • No reallocation: Can't resize arena at runtime
    • Manual management: Requires careful memory management

    Note: Scene Arena is an experimental feature. Use object pooling for most cases.

    "},{"location":"manual/optimization/memory_management/#best-practices","title":"Best Practices","text":""},{"location":"manual/optimization/memory_management/#avoid-dynamic-allocation-in-game-loop","title":"Avoid Dynamic Allocation in Game Loop","text":"
    // \u274c BAD: Allocates every frame\nvoid update(unsigned long deltaTime) override {\n    if (shouldSpawnEnemy) {\n        EnemyActor* enemy = new EnemyActor(x, y);\n        addEntity(enemy);\n    }\n}\n\n// \u2705 GOOD: Use pool\nvoid update(unsigned long deltaTime) override {\n    if (shouldSpawnEnemy) {\n        EnemyActor* enemy = enemyPool.getAvailable();\n        if (enemy) {\n            enemy->reset(x, y);\n            enemy->isEnabled = true;\n        }\n    }\n}\n
    "},{"location":"manual/optimization/memory_management/#pre-allocate-resources","title":"Pre-allocate Resources","text":"
    class GameScene : public pixelroot32::core::Scene {\nprivate:\n    // Pre-allocated pools\n    ProjectilePool projectiles;\n    EnemyPool enemies;\n    ParticlePool particles;\n\npublic:\n    void init() override {\n        // All pools created in constructor\n        // No allocation in init() or update()\n    }\n};\n
    "},{"location":"manual/optimization/memory_management/#reuse-objects","title":"Reuse Objects","text":"
    class EnemyActor : public pixelroot32::core::Actor {\npublic:\n    void reset(float x, float y) {\n        this->x = x;\n        this->y = y;\n        this->isEnabled = true;\n        this->isVisible = true;\n        this->health = maxHealth;\n        // Reset all state\n    }\n\n    void deactivate() {\n        isEnabled = false;\n        isVisible = false;\n    }\n};\n\n// Usage\nEnemyActor* enemy = enemyPool.getAvailable();\nif (enemy) {\n    enemy->reset(spawnX, spawnY);\n    addEntity(enemy);\n}\n
    "},{"location":"manual/optimization/memory_management/#avoid-strings-and-dynamic-memory","title":"Avoid Strings and Dynamic Memory","text":"
    // \u274c BAD: String allocation\nvoid draw(Renderer& renderer) override {\n    std::string scoreText = \"Score: \" + std::to_string(score);\n    renderer.drawText(scoreText.c_str(), 10, 10, Color::White, 1);\n}\n\n// \u2705 GOOD: Static buffer\nvoid draw(Renderer& renderer) override {\n    char scoreBuffer[32];\n    snprintf(scoreBuffer, sizeof(scoreBuffer), \"Score: %d\", score);\n    renderer.drawText(scoreBuffer, 10, 10, Color::White, 1);\n}\n
    "},{"location":"manual/optimization/memory_management/#store-data-in-flash","title":"Store Data in Flash","text":"
    // \u2705 GOOD: Stored in flash (const/constexpr)\nstatic const uint16_t SPRITE_DATA[] = {\n    0b00111100,\n    0b01111110,\n    // ...\n};\n\n// \u274c BAD: Stored in RAM\nuint16_t spriteData[] = {\n    0b00111100,\n    0b01111110,\n    // ...\n};\n
    "},{"location":"manual/optimization/memory_management/#memory-monitoring","title":"Memory Monitoring","text":""},{"location":"manual/optimization/memory_management/#check-available-memory","title":"Check Available Memory","text":"
    #ifdef PLATFORM_ESP32\n#include <Arduino.h>\n\nvoid checkMemory() {\n    Serial.print(\"Free heap: \");\n    Serial.println(ESP.getFreeHeap());\n    Serial.print(\"Largest free block: \");\n    Serial.println(ESP.getMaxAllocHeap());\n}\n#endif\n
    "},{"location":"manual/optimization/memory_management/#monitor-entity-count","title":"Monitor Entity Count","text":"
    void update(unsigned long deltaTime) override {\n    Scene::update(deltaTime);\n\n    // Check entity count\n    int entityCount = getEntityCount();\n    if (entityCount >= MAX_ENTITIES) {\n        Serial.println(\"WARNING: Entity limit reached!\");\n    }\n}\n
    "},{"location":"manual/optimization/memory_management/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/optimization/memory_management/#entity-lifecycle-management","title":"Entity Lifecycle Management","text":"
    class ManagedEntity {\nprivate:\n    bool isActive = false;\n\npublic:\n    void activate(float x, float y) {\n        this->x = x;\n        this->y = y;\n        isActive = true;\n        isEnabled = true;\n        isVisible = true;\n    }\n\n    void deactivate() {\n        isActive = false;\n        isEnabled = false;\n        isVisible = false;\n    }\n\n    bool getIsActive() const { return isActive; }\n};\n\n// Pool manages lifecycle\nclass EntityManager {\nprivate:\n    EntityPool<ManagedEntity, 20> pool;\n\npublic:\n    ManagedEntity* spawn(float x, float y) {\n        auto* entity = pool.acquire();\n        if (entity) {\n            entity->activate(x, y);\n        }\n        return entity;\n    }\n\n    void despawn(ManagedEntity* entity) {\n        if (entity) {\n            entity->deactivate();\n            pool.release(entity);\n        }\n    }\n};\n
    "},{"location":"manual/optimization/memory_management/#memory-efficient-collections","title":"Memory-Efficient Collections","text":"
    // Fixed-size array instead of vector\nclass EntityArray {\nprivate:\n    static const int MAX_SIZE = 32;\n    pixelroot32::core::Entity* entities[MAX_SIZE];\n    int count = 0;\n\npublic:\n    bool add(pixelroot32::core::Entity* entity) {\n        if (count >= MAX_SIZE) return false;\n        entities[count++] = entity;\n        return true;\n    }\n\n    void remove(pixelroot32::core::Entity* entity) {\n        for (int i = 0; i < count; i++) {\n            if (entities[i] == entity) {\n                entities[i] = entities[--count];\n                break;\n            }\n        }\n    }\n\n    int size() const { return count; }\n    pixelroot32::core::Entity* operator[](int index) { return entities[index]; }\n};\n
    "},{"location":"manual/optimization/memory_management/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/optimization/memory_management/#out-of-memory-errors","title":"Out of Memory Errors","text":"
    • Reduce pool sizes
    • Use fewer entities
    • Store more data in flash
    • Avoid dynamic allocation
    • Check for memory leaks
    "},{"location":"manual/optimization/memory_management/#entity-limit-reached","title":"Entity Limit Reached","text":"
    • MAX_ENTITIES = 32 is a hard limit
    • Use object pooling to reuse entities
    • Deactivate entities instead of removing
    • Combine multiple entities into one
    "},{"location":"manual/optimization/memory_management/#memory-fragmentation","title":"Memory Fragmentation","text":"
    • Use object pooling
    • Pre-allocate all resources
    • Avoid frequent new/delete
    • Consider Scene Arena (experimental)
    "},{"location":"manual/optimization/memory_management/#next-steps","title":"Next Steps","text":"

    Now that you understand memory management, learn about: - Performance Optimization - Improve game performance - Platforms and Drivers - Understand platform specifics - Extensibility - Extend the engine

    See also: - API Reference - Scene - Manual - Scenes and Entities

    "},{"location":"manual/optimization/performance_tuning/","title":"Performance Optimization","text":"

    This guide covers techniques to improve game performance on ESP32, including rendering optimization, logic optimization, and profiling.

    "},{"location":"manual/optimization/performance_tuning/#esp32-performance-characteristics","title":"ESP32 Performance Characteristics","text":""},{"location":"manual/optimization/performance_tuning/#cpu-limitations","title":"CPU Limitations","text":"
    • Dual-core: 240MHz (typically)
    • Single-threaded game loop: One core handles everything
    • Target FPS: 30-60 FPS (depends on game complexity)
    • Frame budget: ~16-33ms per frame at 60 FPS
    "},{"location":"manual/optimization/performance_tuning/#common-bottlenecks","title":"Common Bottlenecks","text":"
    1. Rendering: Too many draw calls
    2. Collision detection: Too many collision checks
    3. Memory allocation: Dynamic allocation in game loop
    4. Complex calculations: Expensive math operations
    5. String operations: String concatenation/formatting
    "},{"location":"manual/optimization/performance_tuning/#rendering-optimization","title":"Rendering Optimization","text":""},{"location":"manual/optimization/performance_tuning/#viewport-culling","title":"Viewport Culling","text":"

    Only draw entities that are visible on screen:

    bool isOnScreen(float x, float y, int width, int height, \n                const Camera2D& camera) {\n    float cameraX = camera.getX();\n    float cameraY = camera.getY();\n    int screenWidth = engine.getRenderer().getWidth();\n    int screenHeight = engine.getRenderer().getHeight();\n\n    return !(x + width < cameraX || \n             x > cameraX + screenWidth ||\n             y + height < cameraY || \n             y > cameraY + screenHeight);\n}\n\nvoid draw(pixelroot32::graphics::Renderer& renderer) override {\n    camera.apply(renderer);\n\n    // Only draw visible entities\n    for (auto* entity : entities) {\n        if (entity->isVisible && \n            isOnScreen(entity->x, entity->y, entity->width, entity->height, camera)) {\n            entity->draw(renderer);\n        }\n    }\n}\n
    "},{"location":"manual/optimization/performance_tuning/#reduce-draw-calls","title":"Reduce Draw Calls","text":"

    Batch similar operations:

    // \u274c BAD: Many individual draw calls\nvoid drawBackground(Renderer& renderer) {\n    for (int y = 0; y < 30; y++) {\n        for (int x = 0; x < 30; x++) {\n            renderer.drawSprite(tile, x * 8, y * 8, Color::White);\n        }\n    }\n}\n\n// \u2705 GOOD: Use tilemap (single call)\nvoid drawBackground(Renderer& renderer) {\n    renderer.drawTileMap(backgroundTileMap, 0, 0, Color::White);\n}\n
    "},{"location":"manual/optimization/performance_tuning/#optimize-sprite-drawing","title":"Optimize Sprite Drawing","text":"
    • Reuse sprites: Define once, use many times
    • Use 1bpp: Most efficient format
    • Limit sprite size: Smaller sprites = faster drawing
    • Avoid flipping: Flipping has overhead
    "},{"location":"manual/optimization/performance_tuning/#efficient-render-layers","title":"Efficient Render Layers","text":"
    // Organize by layer to minimize layer switches\nvoid draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Draw all layer 0 entities\n    for (auto* entity : layer0Entities) {\n        if (entity->isVisible) entity->draw(renderer);\n    }\n\n    // Draw all layer 1 entities\n    for (auto* entity : layer1Entities) {\n        if (entity->isVisible) entity->draw(renderer);\n    }\n\n    // Draw all layer 2 entities\n    for (auto* entity : layer2Entities) {\n        if (entity->isVisible) entity->draw(renderer);\n    }\n}\n
    "},{"location":"manual/optimization/performance_tuning/#logic-optimization","title":"Logic Optimization","text":""},{"location":"manual/optimization/performance_tuning/#reduce-calculations-per-frame","title":"Reduce Calculations Per Frame","text":"

    Cache expensive calculations:

    class OptimizedActor : public pixelroot32::core::Actor {\nprivate:\n    float cachedDistance = 0.0f;\n    bool distanceDirty = true;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        // Only recalculate when position changes\n        if (distanceDirty) {\n            cachedDistance = sqrt(x * x + y * y);\n            distanceDirty = false;\n        }\n\n        // Use cached value\n        if (cachedDistance > maxDistance) {\n            // ...\n        }\n    }\n\n    void setPosition(float newX, float newY) {\n        x = newX;\n        y = newY;\n        distanceDirty = true; // Mark for recalculation\n    }\n};\n
    "},{"location":"manual/optimization/performance_tuning/#lazy-evaluation","title":"Lazy Evaluation","text":"

    Only calculate when needed:

    class LazyCalculator {\nprivate:\n    mutable bool cached = false;\n    mutable float cachedValue = 0.0f;\n\npublic:\n    float getValue() const {\n        if (!cached) {\n            cachedValue = expensiveCalculation();\n            cached = true;\n        }\n        return cachedValue;\n    }\n\n    void invalidate() {\n        cached = false;\n    }\n};\n
    "},{"location":"manual/optimization/performance_tuning/#avoid-expensive-operations","title":"Avoid Expensive Operations","text":"
    // \u274c BAD: sqrt() every frame\nfloat distance = sqrt(dx * dx + dy * dy);\n\n// \u2705 GOOD: Use squared distance\nfloat distanceSq = dx * dx + dy * dy;\nif (distanceSq > maxDistanceSq) {\n    // ...\n}\n\n// \u274c BAD: sin/cos every frame\nfloat x = cos(angle) * radius;\nfloat y = sin(angle) * radius;\n\n// \u2705 GOOD: Pre-calculate or use lookup table\nstatic const float COS_TABLE[360] = { /* ... */ };\nstatic const float SIN_TABLE[360] = { /* ... */ };\nfloat x = COS_TABLE[static_cast<int>(angle) % 360] * radius;\n
    "},{"location":"manual/optimization/performance_tuning/#collision-optimization","title":"Collision Optimization","text":""},{"location":"manual/optimization/performance_tuning/#use-collision-layers-efficiently","title":"Use Collision Layers Efficiently","text":"
    // \u274c BAD: Check everything against everything\nfor (auto* actor1 : actors) {\n    for (auto* actor2 : actors) {\n        if (actor1 != actor2) {\n            checkCollision(actor1, actor2);\n        }\n    }\n}\n\n// \u2705 GOOD: Use layers to reduce checks\n// CollisionSystem automatically uses layers\n// Only actors with matching layers/masks are checked\n
    "},{"location":"manual/optimization/performance_tuning/#reduce-collision-checks","title":"Reduce Collision Checks","text":"
    // Only check collisions for active actors\nvoid update(unsigned long deltaTime) override {\n    for (auto* actor : actors) {\n        if (actor->isEnabled && actor->isActive) {\n            actor->update(deltaTime);\n        }\n    }\n\n    // CollisionSystem only checks enabled actors\n    Scene::update(deltaTime);\n}\n
    "},{"location":"manual/optimization/performance_tuning/#simple-hitboxes","title":"Simple Hitboxes","text":"
    // \u2705 GOOD: Simple AABB\npixelroot32::core::Rect getHitBox() override {\n    return {x, y, width, height};\n}\n\n// \u274c BAD: Complex shape calculations\npixelroot32::core::Rect getHitBox() override {\n    // Complex polygon calculations...\n}\n
    "},{"location":"manual/optimization/performance_tuning/#string-optimization","title":"String Optimization","text":""},{"location":"manual/optimization/performance_tuning/#avoid-string-operations","title":"Avoid String Operations","text":"
    // \u274c BAD: String concatenation\nstd::string text = \"Score: \" + std::to_string(score);\n\n// \u2705 GOOD: Static buffer with snprintf\nchar buffer[32];\nsnprintf(buffer, sizeof(buffer), \"Score: %d\", score);\nrenderer.drawText(buffer, 10, 10, Color::White, 1);\n
    "},{"location":"manual/optimization/performance_tuning/#cache-text-rendering","title":"Cache Text Rendering","text":"
    class CachedText {\nprivate:\n    char buffer[32];\n    bool dirty = true;\n    int lastValue = -1;\n\npublic:\n    void update(int value) {\n        if (value != lastValue) {\n            snprintf(buffer, sizeof(buffer), \"Score: %d\", value);\n            lastValue = value;\n            dirty = true;\n        }\n    }\n\n    void draw(Renderer& renderer, int x, int y) {\n        if (dirty) {\n            renderer.drawText(buffer, x, y, Color::White, 1);\n            dirty = false;\n        }\n    }\n};\n
    "},{"location":"manual/optimization/performance_tuning/#profiling","title":"Profiling","text":""},{"location":"manual/optimization/performance_tuning/#measure-frame-time","title":"Measure Frame Time","text":"
    class PerformanceMonitor {\nprivate:\n    unsigned long frameTime = 0;\n    unsigned long maxFrameTime = 0;\n    unsigned long frameCount = 0;\n\npublic:\n    void startFrame() {\n        frameTime = millis();\n    }\n\n    void endFrame() {\n        unsigned long elapsed = millis() - frameTime;\n        if (elapsed > maxFrameTime) {\n            maxFrameTime = elapsed;\n        }\n        frameCount++;\n\n        // Log every 60 frames\n        if (frameCount % 60 == 0) {\n            Serial.print(\"Max frame time: \");\n            Serial.println(maxFrameTime);\n            maxFrameTime = 0;\n        }\n    }\n};\n\n// Usage\nPerformanceMonitor perf;\n\nvoid update(unsigned long deltaTime) override {\n    perf.startFrame();\n    Scene::update(deltaTime);\n    perf.endFrame();\n}\n
    "},{"location":"manual/optimization/performance_tuning/#identify-bottlenecks","title":"Identify Bottlenecks","text":"
    #ifdef PLATFORM_ESP32\n#include <Arduino.h>\n\nclass Profiler {\nprivate:\n    unsigned long updateTime = 0;\n    unsigned long drawTime = 0;\n    unsigned long collisionTime = 0;\n\npublic:\n    void startUpdate() {\n        updateTime = micros();\n    }\n\n    void endUpdate() {\n        updateTime = micros() - updateTime;\n    }\n\n    void startDraw() {\n        drawTime = micros();\n    }\n\n    void endDraw() {\n        drawTime = micros() - drawTime;\n    }\n\n    void log() {\n        Serial.print(\"Update: \");\n        Serial.print(updateTime);\n        Serial.print(\"us, Draw: \");\n        Serial.print(drawTime);\n        Serial.println(\"us\");\n    }\n};\n#endif\n
    "},{"location":"manual/optimization/performance_tuning/#best-practices-summary","title":"Best Practices Summary","text":""},{"location":"manual/optimization/performance_tuning/#rendering","title":"Rendering","text":"
    • \u2705 Use viewport culling
    • \u2705 Batch similar draw operations
    • \u2705 Use tilemaps for backgrounds
    • \u2705 Limit sprite count and size
    • \u2705 Organize by render layers
    "},{"location":"manual/optimization/performance_tuning/#logic","title":"Logic","text":"
    • \u2705 Cache expensive calculations
    • \u2705 Use lazy evaluation
    • \u2705 Avoid sqrt/sin/cos in loops
    • \u2705 Pre-calculate lookup tables
    • \u2705 Reduce update frequency for non-critical entities
    "},{"location":"manual/optimization/performance_tuning/#memory","title":"Memory","text":"
    • \u2705 Use object pooling
    • \u2705 Avoid dynamic allocation
    • \u2705 Store data in flash
    • \u2705 Reuse objects
    "},{"location":"manual/optimization/performance_tuning/#collisions","title":"Collisions","text":"
    • \u2705 Use collision layers efficiently
    • \u2705 Only check active entities
    • \u2705 Use simple hitboxes
    • \u2705 Limit active collision pairs
    "},{"location":"manual/optimization/performance_tuning/#strings","title":"Strings","text":"
    • \u2705 Use static buffers
    • \u2705 Cache text rendering
    • \u2705 Avoid string operations in loops
    "},{"location":"manual/optimization/performance_tuning/#common-optimization-patterns","title":"Common Optimization Patterns","text":""},{"location":"manual/optimization/performance_tuning/#update-frequency-reduction","title":"Update Frequency Reduction","text":"
    class LowFrequencyUpdater {\nprivate:\n    unsigned long timer = 0;\n    unsigned long interval = 100; // Update every 100ms\n\npublic:\n    void update(unsigned long deltaTime) {\n        timer += deltaTime;\n        if (timer >= interval) {\n            timer -= interval;\n            // Do expensive update\n            expensiveUpdate();\n        }\n    }\n};\n
    "},{"location":"manual/optimization/performance_tuning/#spatial-partitioning-simple","title":"Spatial Partitioning (Simple)","text":"
    // Divide screen into zones\nclass SpatialGrid {\nprivate:\n    static const int GRID_SIZE = 4;\n    static const int CELL_WIDTH = 60;\n    static const int CELL_HEIGHT = 60;\n\n    std::vector<Actor*> grid[GRID_SIZE][GRID_SIZE];\n\npublic:\n    void add(Actor* actor) {\n        int cellX = static_cast<int>(actor->x) / CELL_WIDTH;\n        int cellY = static_cast<int>(actor->y) / CELL_HEIGHT;\n        if (cellX >= 0 && cellX < GRID_SIZE && \n            cellY >= 0 && cellY < GRID_SIZE) {\n            grid[cellY][cellX].push_back(actor);\n        }\n    }\n\n    void checkCollisions() {\n        // Only check collisions within same cell\n        for (int y = 0; y < GRID_SIZE; y++) {\n            for (int x = 0; x < GRID_SIZE; x++) {\n                auto& cell = grid[y][x];\n                for (size_t i = 0; i < cell.size(); i++) {\n                    for (size_t j = i + 1; j < cell.size(); j++) {\n                        checkCollision(cell[i], cell[j]);\n                    }\n                }\n            }\n        }\n    }\n};\n
    "},{"location":"manual/optimization/performance_tuning/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/optimization/performance_tuning/#low-fps","title":"Low FPS","text":"
    • Profile to find bottlenecks
    • Reduce entity count
    • Optimize rendering (culling, batching)
    • Simplify collision detection
    • Reduce update frequency
    "},{"location":"manual/optimization/performance_tuning/#frame-drops","title":"Frame Drops","text":"
    • Check for expensive operations in update()
    • Avoid dynamic allocation
    • Cache calculations
    • Reduce draw calls
    "},{"location":"manual/optimization/performance_tuning/#stuttering","title":"Stuttering","text":"
    • Ensure frame-rate independence (use deltaTime)
    • Avoid blocking operations
    • Pre-load resources
    • Use object pooling
    "},{"location":"manual/optimization/performance_tuning/#next-steps","title":"Next Steps","text":"

    Now that you understand performance optimization, learn about: - Memory Management - Manage memory efficiently - Platforms and Drivers - Platform-specific optimizations - Extensibility - Extend the engine

    See also: - Manual - Basic Rendering - Manual - Physics and Collisions

    "},{"location":"manual/optimization/platforms_and_drivers/","title":"Platforms and Drivers","text":"

    PixelRoot32 supports multiple platforms through driver abstraction. This guide covers supported platforms, display drivers, audio backends, and build configuration.

    "},{"location":"manual/optimization/platforms_and_drivers/#supported-platforms","title":"Supported Platforms","text":""},{"location":"manual/optimization/platforms_and_drivers/#esp32","title":"ESP32","text":"

    Primary platform for PixelRoot32 games.

    Characteristics: - TFT_eSPI display driver - Internal DAC or I2S audio - GPIO button input - Limited RAM/Flash - Real hardware constraints

    Use for: - Final game deployment - Hardware testing - Production builds

    "},{"location":"manual/optimization/platforms_and_drivers/#nativedesktop-sdl2","title":"Native/Desktop (SDL2)","text":"

    Development platform for rapid iteration.

    Characteristics: - SDL2 display driver - SDL2 audio backend - Keyboard input - Unlimited resources (for testing) - Fast development cycle

    Use for: - Development and debugging - Testing without hardware - Rapid prototyping - CI/CD testing

    "},{"location":"manual/optimization/platforms_and_drivers/#display-drivers","title":"Display Drivers","text":""},{"location":"manual/optimization/platforms_and_drivers/#tft_espi-esp32","title":"TFT_eSPI (ESP32)","text":"

    TFT_eSPI is the display driver for ESP32, supporting many TFT displays.

    "},{"location":"manual/optimization/platforms_and_drivers/#configuration","title":"Configuration","text":"

    Configure TFT_eSPI via build flags in platformio.ini:

    [env:esp32dev]\nbuild_flags = \n    -D ST7789_DRIVER          # Display type\n    -D TFT_WIDTH=240          # Display width\n    -D TFT_HEIGHT=240         # Display height\n    -D TFT_MOSI=23            # SPI MOSI pin\n    -D TFT_SCLK=18            # SPI clock pin\n    -D TFT_DC=2               # Data/Command pin\n    -D TFT_RST=4              # Reset pin\n    -D TFT_CS=-1              # Chip select (-1 if not used)\n    -D SPI_FREQUENCY=40000000 # SPI frequency\n
    "},{"location":"manual/optimization/platforms_and_drivers/#supported-displays","title":"Supported Displays","text":"
    • ST7735: 128x128, 128x160
    • ST7789: 240x240, 240x320
    • ILI9341: 240x320
    • And more: See TFT_eSPI documentation
    "},{"location":"manual/optimization/platforms_and_drivers/#usage","title":"Usage","text":"
    #include <drivers/esp32/TFT_eSPI_Drawer.h>\n\n// Display configuration\npixelroot32::graphics::DisplayConfig displayConfig(\n    pixelroot32::graphics::DisplayType::ST7789,\n    0,      // rotation\n    240,    // width\n    240     // height\n);\n\n// TFT_eSPI_Drawer is created automatically by Engine\n// No manual driver creation needed\n
    "},{"location":"manual/optimization/platforms_and_drivers/#sdl2_drawer-native","title":"SDL2_Drawer (Native)","text":"

    SDL2_Drawer provides display output for PC/desktop development.

    "},{"location":"manual/optimization/platforms_and_drivers/#configuration_1","title":"Configuration","text":"
    #include <drivers/native/SDL2_Drawer.h>\n\n// Display configuration (NONE defaults to SDL2)\npixelroot32::graphics::DisplayConfig displayConfig(\n    pixelroot32::graphics::DisplayType::NONE,\n    0,      // rotation\n    240,    // width\n    240     // height\n);\n\n// SDL2_Drawer is created automatically\n
    "},{"location":"manual/optimization/platforms_and_drivers/#sdl2-installation","title":"SDL2 Installation","text":"

    Windows (MSYS2):

    pacman -S mingw-w64-x86_64-SDL2\n

    Linux:

    sudo apt-get install libsdl2-dev\n

    macOS:

    brew install sdl2\n

    "},{"location":"manual/optimization/platforms_and_drivers/#audio-backends","title":"Audio Backends","text":""},{"location":"manual/optimization/platforms_and_drivers/#esp32_dac_audiobackend","title":"ESP32_DAC_AudioBackend","text":"

    Uses ESP32's internal DAC for audio output.

    "},{"location":"manual/optimization/platforms_and_drivers/#configuration_2","title":"Configuration","text":"
    #include <drivers/esp32/ESP32_DAC_AudioBackend.h>\n\nconst int DAC_PIN = 25; // GPIO 25 or 26\npixelroot32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(\n    DAC_PIN,    // DAC pin (25 or 26)\n    11025       // Sample rate (Hz)\n);\n\npixelroot32::audio::AudioConfig audioConfig(\n    &audioBackend, \n    audioBackend.getSampleRate()\n);\n

    Characteristics: - Simple setup (just one pin) - Lower quality than I2S - Good for basic audio - Sample rate: 11025 Hz recommended

    "},{"location":"manual/optimization/platforms_and_drivers/#esp32_i2s_audiobackend","title":"ESP32_I2S_AudioBackend","text":"

    Uses ESP32's I2S peripheral for higher quality audio.

    "},{"location":"manual/optimization/platforms_and_drivers/#configuration_3","title":"Configuration","text":"
    #include <drivers/esp32/ESP32_I2S_AudioBackend.h>\n\nconst int I2S_BCLK = 26;  // Bit clock pin\nconst int I2S_LRCK = 25;  // Left/Right clock pin\nconst int I2S_DOUT = 22;  // Data out pin\n\npixelroot32::drivers::esp32::ESP32_I2S_AudioBackend audioBackend(\n    I2S_BCLK,\n    I2S_LRCK,\n    I2S_DOUT,\n    22050  // Sample rate (Hz)\n);\n\npixelroot32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n

    Characteristics: - Higher quality than DAC - Requires external I2S DAC (e.g., MAX98357A) - Better for music - Sample rate: 22050 Hz recommended

    "},{"location":"manual/optimization/platforms_and_drivers/#sdl2_audiobackend-native","title":"SDL2_AudioBackend (Native)","text":"

    SDL2 audio for PC development.

    "},{"location":"manual/optimization/platforms_and_drivers/#configuration_4","title":"Configuration","text":"
    #include <drivers/native/SDL2_AudioBackend.h>\n\npixelroot32::drivers::native::SDL2_AudioBackend audioBackend(\n    22050,  // Sample rate\n    1024    // Buffer size\n);\n\npixelroot32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n
    "},{"location":"manual/optimization/platforms_and_drivers/#build-flags","title":"Build Flags","text":""},{"location":"manual/optimization/platforms_and_drivers/#experimental-features","title":"Experimental Features","text":"

    Enable experimental features with build flags:

    [env:esp32dev]\nbuild_flags = \n    -D PIXELROOT32_ENABLE_2BPP_SPRITES    # Enable 2bpp sprite format\n    -D PIXELROOT32_ENABLE_4BPP_SPRITES   # Enable 4bpp sprite format\n    -D PIXELROOT32_ENABLE_SCENE_ARENA    # Enable Scene Arena (experimental)\n
    "},{"location":"manual/optimization/platforms_and_drivers/#platform-detection","title":"Platform Detection","text":"
    #ifdef PLATFORM_ESP32\n    // ESP32-specific code\n    Serial.println(\"Running on ESP32\");\n#endif\n\n#ifdef PLATFORM_NATIVE\n    // Native/PC-specific code\n    printf(\"Running on PC\\n\");\n#endif\n
    "},{"location":"manual/optimization/platforms_and_drivers/#optimization-flags","title":"Optimization Flags","text":"
    [env:esp32dev]\nbuild_flags = \n    -O2              # Optimization level\n    -ffunction-sections\n    -fdata-sections\n    -Wl,--gc-sections\n
    "},{"location":"manual/optimization/platforms_and_drivers/#complete-platform-setup-examples","title":"Complete Platform Setup Examples","text":""},{"location":"manual/optimization/platforms_and_drivers/#esp32-complete-setup","title":"ESP32 Complete Setup","text":"
    #include <Arduino.h>\n#include <core/Engine.h>\n#include <drivers/esp32/TFT_eSPI_Drawer.h>\n#include <drivers/esp32/ESP32_DAC_AudioBackend.h>\n\nnamespace pr32 = pixelroot32;\n\n// Audio\nconst int DAC_PIN = 25;\npr32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(DAC_PIN, 11025);\n\n// Display\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::ST7789,\n    0, 240, 240\n);\n\n// Input\npr32::input::InputConfig inputConfig(\n    6, 32, 27, 33, 14, 13, 12  // 6 buttons, pins\n);\n\n// Audio config\npr32::audio::AudioConfig audioConfig(&audioBackend, 11025);\n\n// Engine\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\nvoid setup() {\n    Serial.begin(115200);\n    engine.init();\n    // ... scene setup\n}\n\nvoid loop() {\n    engine.run();\n}\n
    "},{"location":"manual/optimization/platforms_and_drivers/#native-complete-setup","title":"Native Complete Setup","text":"
    #define SDL_MAIN_HANDLED\n#include <SDL2/SDL.h>\n#include <core/Engine.h>\n#include <drivers/native/SDL2_Drawer.h>\n#include <drivers/native/SDL2_AudioBackend.h>\n\nnamespace pr32 = pixelroot32;\n\n// Audio\npr32::drivers::native::SDL2_AudioBackend audioBackend(22050, 1024);\n\n// Display\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::NONE,\n    0, 240, 240\n);\n\n// Input (SDL scancodes)\npr32::input::InputConfig inputConfig(\n    6,\n    SDL_SCANCODE_UP,\n    SDL_SCANCODE_DOWN,\n    SDL_SCANCODE_LEFT,\n    SDL_SCANCODE_RIGHT,\n    SDL_SCANCODE_SPACE,\n    SDL_SCANCODE_RETURN\n);\n\n// Audio config\npr32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n\n// Engine\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\nint main(int argc, char* argv[]) {\n    engine.init();\n    // ... scene setup\n    engine.run();\n    return 0;\n}\n
    "},{"location":"manual/optimization/platforms_and_drivers/#platform-specific-considerations","title":"Platform-Specific Considerations","text":""},{"location":"manual/optimization/platforms_and_drivers/#esp32_1","title":"ESP32","text":"

    Memory: - Limited RAM (~320KB) - Use object pooling - Store data in flash - Avoid dynamic allocation

    Performance: - Target 30-60 FPS - Optimize rendering - Reduce entity count - Profile on hardware

    Hardware: - GPIO pin configuration - SPI display setup - Audio hardware connections - Power considerations

    "},{"location":"manual/optimization/platforms_and_drivers/#native","title":"Native","text":"

    Development: - Fast iteration - Easy debugging - Unlimited resources - Visual debugging tools

    Testing: - Test logic without hardware - Rapid prototyping - CI/CD integration - Cross-platform testing

    "},{"location":"manual/optimization/platforms_and_drivers/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/optimization/platforms_and_drivers/#esp32-display-issues","title":"ESP32 Display Issues","text":"
    • Check wiring connections
    • Verify pin numbers
    • Lower SPI frequency
    • Check display type matches
    • Verify power supply
    "},{"location":"manual/optimization/platforms_and_drivers/#esp32-audio-issues","title":"ESP32 Audio Issues","text":"
    • Check DAC/I2S pin configuration
    • Verify sample rate
    • Check hardware connections
    • Lower volume if distorted
    • Test with different sample rates
    "},{"location":"manual/optimization/platforms_and_drivers/#native-build-issues","title":"Native Build Issues","text":"
    • Verify SDL2 installation
    • Check include/library paths
    • Ensure SDL2 version compatibility
    • Check linker flags
    "},{"location":"manual/optimization/platforms_and_drivers/#next-steps","title":"Next Steps","text":"

    Now that you understand platforms and drivers, learn about: - Extensibility - Create custom drivers - Memory Management - ESP32 memory constraints - Performance Optimization - Platform-specific optimization

    See also: - API Reference - DrawSurface - API Reference - AudioBackend - Getting Started - Your First Project

    "},{"location":"reference/api_overview/","title":"API Reference Overview","text":"

    This document provides a complete technical reference for all PixelRoot32 APIs, organized by module. Each class includes descriptions, constructors, methods, properties, and usage examples.

    "},{"location":"reference/api_overview/#organization","title":"Organization","text":"

    The API is organized into the following modules:

    • Core: Engine, Scene, Entity, Actor, PhysicsActor, SceneManager
    • Graphics: Renderer, Camera2D, Color, Font, Sprite, TileMap, DrawSurface
    • Audio: AudioEngine, MusicPlayer, AudioTypes, AudioConfig, AudioBackend
    • Input: InputManager, InputConfig
    • Physics: CollisionSystem, CollisionTypes
    • UI: UIElement, UIButton, UILabel, UILayouts
    • Particles: ParticleEmitter, ParticleConfig, ParticlePresets
    "},{"location":"reference/api_overview/#quick-navigation","title":"Quick Navigation","text":""},{"location":"reference/api_overview/#core-module","title":"Core Module","text":"
    • Engine - Main engine class, game loop management
    • Scene - Scene/level management
    • Entity - Base game object class
    • Actor - Entity with collision support
    • PhysicsActor - Actor with automatic physics
    • InputManager - Input handling
    • InputConfig - Input configuration
    "},{"location":"reference/api_overview/#graphics-module","title":"Graphics Module","text":"
    • Renderer - High-level rendering API
    • Camera2D - 2D camera for scrolling
    • Color - Color constants and utilities
    • Font - Bitmap font system
    • Sprite - Sprite structures and formats
    • TileMap - Tilemap structure
    • DisplayConfig - Display configuration
    "},{"location":"reference/api_overview/#audio-module","title":"Audio Module","text":"
    • AudioEngine - Sound effects playback
    • MusicPlayer - Background music playback
    • AudioTypes - Audio data structures
    • AudioConfig - Audio configuration
    "},{"location":"reference/api_overview/#physics-module","title":"Physics Module","text":"
    • CollisionSystem - Collision detection
    • CollisionTypes - Collision primitives
    "},{"location":"reference/api_overview/#ui-module","title":"UI Module","text":"
    • UIElement - Base UI element class
    • UIButton - Clickable button
    • UILabel - Text label
    • UILayouts - Layout containers
    "},{"location":"reference/api_overview/#api-documentation-format","title":"API Documentation Format","text":"

    Each API reference page follows this structure:

    "},{"location":"reference/api_overview/#class-name","title":"Class Name","text":"

    Brief description of the class and its purpose.

    "},{"location":"reference/api_overview/#namespace","title":"Namespace","text":"
    namespace pixelroot32::module {\n    class ClassName {\n        // ...\n    };\n}\n
    "},{"location":"reference/api_overview/#constructors","title":"Constructors","text":"

    List of all constructors with parameters.

    "},{"location":"reference/api_overview/#public-methods","title":"Public Methods","text":"Method Description Parameters Returns methodName() Description param: type return type"},{"location":"reference/api_overview/#properties","title":"Properties","text":"Property Type Description property type Description"},{"location":"reference/api_overview/#usage-example","title":"Usage Example","text":"
    // Example code showing typical usage\n
    "},{"location":"reference/api_overview/#performance-notes","title":"Performance Notes","text":"

    Any performance considerations or limitations.

    "},{"location":"reference/api_overview/#see-also","title":"See Also","text":"

    Links to related APIs and documentation.

    "},{"location":"reference/api_overview/#finding-apis","title":"Finding APIs","text":""},{"location":"reference/api_overview/#by-functionality","title":"By Functionality","text":"
    • Game Loop: See Engine
    • Rendering: See Renderer
    • Input: See InputManager
    • Audio: See AudioEngine and MusicPlayer
    • Physics: See PhysicsActor and CollisionSystem
    • UI: See UIElement and layouts
    "},{"location":"reference/api_overview/#by-module","title":"By Module","text":"

    Navigate to the specific module folder: - api_reference/core/ - Core engine classes - api_reference/graphics/ - Rendering and graphics - api_reference/audio/ - Audio system - api_reference/physics/ - Physics and collisions - api_reference/ui/ - User interface

    "},{"location":"reference/api_overview/#complete-api-list","title":"Complete API List","text":""},{"location":"reference/api_overview/#core","title":"Core","text":"
    • Engine
    • Scene
    • Entity
    • Actor
    • PhysicsActor
    • InputManager
    • InputConfig
    "},{"location":"reference/api_overview/#graphics","title":"Graphics","text":"
    • Renderer
    • Camera2D
    • Color
    • Font
    • Sprite
    • TileMap
    • DisplayConfig
    "},{"location":"reference/api_overview/#audio","title":"Audio","text":"
    • AudioEngine
    • MusicPlayer
    • AudioTypes
    • AudioConfig
    "},{"location":"reference/api_overview/#physics","title":"Physics","text":"
    • CollisionSystem
    • CollisionTypes
    "},{"location":"reference/api_overview/#ui","title":"UI","text":"
    • UIElement
    • UIButton
    • UILabel
    • UIVerticalLayout
    • UIHorizontalLayout
    • UIGridLayout
    • UIAnchorLayout
    • UIPanel
    • UIPaddingContainer
    "},{"location":"reference/api_overview/#related-documentation","title":"Related Documentation","text":"
    • Manual - Game Development - How to use the APIs
    • Manual - Advanced Graphics - Advanced techniques
    • Code Examples - Reusable code snippets
    • Game Examples Guide - Learn from complete games

    Note: This is an overview. For detailed API documentation, see the individual reference pages linked above.

    "},{"location":"reference/code_examples/","title":"Code Examples","text":"

    A library of reusable code snippets for common PixelRoot32 tasks. All examples are complete and functional.

    "},{"location":"reference/code_examples/#initialization","title":"Initialization","text":""},{"location":"reference/code_examples/#basic-engine-setup-esp32","title":"Basic Engine Setup (ESP32)","text":"
    #include <Arduino.h>\n#include <core/Engine.h>\n#include <drivers/esp32/TFT_eSPI_Drawer.h>\n#include <drivers/esp32/ESP32_DAC_AudioBackend.h>\n\nnamespace pr32 = pixelroot32;\n\n// Audio\nconst int DAC_PIN = 25;\npr32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(DAC_PIN, 11025);\n\n// Display\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::ST7789,\n    0, 240, 240\n);\n\n// Input\npr32::input::InputConfig inputConfig(6, 32, 27, 33, 14, 13, 12);\n\n// Audio config\npr32::audio::AudioConfig audioConfig(&audioBackend, 11025);\n\n// Engine\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\nvoid setup() {\n    Serial.begin(115200);\n    engine.init();\n    // ... scene setup\n}\n\nvoid loop() {\n    engine.run();\n}\n
    "},{"location":"reference/code_examples/#basic-engine-setup-native","title":"Basic Engine Setup (Native)","text":"
    #define SDL_MAIN_HANDLED\n#include <SDL2/SDL.h>\n#include <core/Engine.h>\n#include <drivers/native/SDL2_Drawer.h>\n#include <drivers/native/SDL2_AudioBackend.h>\n\nnamespace pr32 = pixelroot32;\n\npr32::drivers::native::SDL2_AudioBackend audioBackend(22050, 1024);\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::NONE, 0, 240, 240\n);\npr32::input::InputConfig inputConfig(\n    6, SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, \n    SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT,\n    SDL_SCANCODE_SPACE, SDL_SCANCODE_RETURN\n);\npr32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\nint main(int argc, char* argv[]) {\n    engine.init();\n    // ... scene setup\n    engine.run();\n    return 0;\n}\n
    "},{"location":"reference/code_examples/#entity-movement","title":"Entity Movement","text":""},{"location":"reference/code_examples/#simple-movement","title":"Simple Movement","text":"
    class MovingEntity : public pixelroot32::core::Entity {\nprivate:\n    float speedX = 50.0f;\n    float speedY = 30.0f;\n\npublic:\n    MovingEntity(float x, float y)\n        : Entity(x, y, 16, 16, pixelroot32::core::EntityType::GENERIC) {\n        setRenderLayer(1);\n    }\n\n    void update(unsigned long deltaTime) override {\n        float dt = deltaTime * 0.001f;\n        x += speedX * dt;\n        y += speedY * dt;\n\n        // Bounce off edges\n        if (x < 0 || x > 224) speedX = -speedX;\n        if (y < 0 || y > 224) speedY = -speedY;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x),\n            static_cast<int>(y),\n            width, height,\n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n};\n
    "},{"location":"reference/code_examples/#input-based-movement","title":"Input-Based Movement","text":"
    class PlayerEntity : public pixelroot32::core::Actor {\nprivate:\n    float speed = 100.0f;\n\npublic:\n    PlayerEntity(float x, float y)\n        : Actor(x, y, 16, 16) {\n        setRenderLayer(1);\n    }\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        float dt = deltaTime * 0.001f;\n\n        if (input.isButtonDown(Buttons::LEFT)) {\n            x -= speed * dt;\n        }\n        if (input.isButtonDown(Buttons::RIGHT)) {\n            x += speed * dt;\n        }\n        if (input.isButtonDown(Buttons::UP)) {\n            y -= speed * dt;\n        }\n        if (input.isButtonDown(Buttons::DOWN)) {\n            y += speed * dt;\n        }\n\n        // Keep on screen\n        if (x < 0) x = 0;\n        if (x > 224) x = 224;\n        if (y < 0) y = 0;\n        if (y > 224) y = 224;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x),\n            static_cast<int>(y),\n            width, height,\n            pixelroot32::graphics::Color::White\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Handle collision\n    }\n};\n
    "},{"location":"reference/code_examples/#collisions","title":"Collisions","text":""},{"location":"reference/code_examples/#basic-collision-detection","title":"Basic Collision Detection","text":"
    class CollidableEntity : public pixelroot32::core::Actor {\npublic:\n    CollidableEntity(float x, float y)\n        : Actor(x, y, 16, 16) {\n        setRenderLayer(1);\n        setCollisionLayer(Layers::PLAYER);\n        setCollisionMask(Layers::ENEMY | Layers::WALL);\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        if (other->isInLayer(Layers::ENEMY)) {\n            // Hit enemy\n            takeDamage();\n        } else if (other->isInLayer(Layers::WALL)) {\n            // Hit wall\n            stopMovement();\n        }\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n};\n
    "},{"location":"reference/code_examples/#collision-layers-setup","title":"Collision Layers Setup","text":"
    // Define in GameLayers.h\nnamespace Layers {\n    constexpr uint16_t PLAYER = 0x0001;\n    constexpr uint16_t ENEMY = 0x0002;\n    constexpr uint16_t PROJECTILE = 0x0004;\n    constexpr uint16_t WALL = 0x0008;\n    constexpr uint16_t PICKUP = 0x0010;\n}\n\n// Usage\nplayer->setCollisionLayer(Layers::PLAYER);\nplayer->setCollisionMask(Layers::ENEMY | Layers::WALL);\n\nenemy->setCollisionLayer(Layers::ENEMY);\nenemy->setCollisionMask(Layers::PLAYER | Layers::PROJECTILE);\n
    "},{"location":"reference/code_examples/#sound-effects","title":"Sound Effects","text":""},{"location":"reference/code_examples/#common-sound-effects","title":"Common Sound Effects","text":"
    namespace SoundEffects {\n    inline pixelroot32::audio::AudioEvent jump() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::PULSE;\n        evt.frequency = 600.0f;\n        evt.duration = 0.1f;\n        evt.volume = 0.7f;\n        evt.duty = 0.25f;\n        return evt;\n    }\n\n    inline pixelroot32::audio::AudioEvent coin() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::PULSE;\n        evt.frequency = 1500.0f;\n        evt.duration = 0.12f;\n        evt.volume = 0.8f;\n        evt.duty = 0.5f;\n        return evt;\n    }\n\n    inline pixelroot32::audio::AudioEvent explosion() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::NOISE;\n        evt.frequency = 200.0f;\n        evt.duration = 0.3f;\n        evt.volume = 0.9f;\n        return evt;\n    }\n}\n\n// Usage\nengine.getAudioEngine().playEvent(SoundEffects::jump());\n
    "},{"location":"reference/code_examples/#playing-sound-on-event","title":"Playing Sound on Event","text":"
    class PlayerActor : public pixelroot32::core::Actor {\npublic:\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n\n        if (input.isButtonPressed(Buttons::A)) {\n            // Play jump sound\n            pixelroot32::audio::AudioEvent jumpSound{};\n            jumpSound.type = pixelroot32::audio::WaveType::PULSE;\n            jumpSound.frequency = 800.0f;\n            jumpSound.duration = 0.1f;\n            jumpSound.volume = 0.7f;\n            jumpSound.duty = 0.25f;\n\n            engine.getAudioEngine().playEvent(jumpSound);\n\n            // Jump logic\n            jump();\n        }\n    }\n};\n
    "},{"location":"reference/code_examples/#ui-components","title":"UI Components","text":""},{"location":"reference/code_examples/#simple-menu","title":"Simple Menu","text":"
    class MenuScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIVerticalLayout* menu;\n\npublic:\n    void init() override {\n        menu = new pixelroot32::graphics::ui::UIVerticalLayout(40, 60, 160, 160);\n        menu->setPadding(10);\n        menu->setSpacing(8);\n        menu->setNavigationButtons(0, 1);\n        menu->setButtonStyle(\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Cyan,\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Black\n        );\n\n        menu->addElement(new pixelroot32::graphics::ui::UIButton(\n            \"Start\", 0, 0, 0, 140, 25, []() { startGame(); }\n        ));\n\n        menu->addElement(new pixelroot32::graphics::ui::UIButton(\n            \"Options\", 1, 0, 0, 140, 25, []() { showOptions(); }\n        ));\n\n        addEntity(menu);\n    }\n\n    void update(unsigned long deltaTime) override {\n        menu->handleInput(engine.getInputManager());\n        Scene::update(deltaTime);\n    }\n};\n
    "},{"location":"reference/code_examples/#hud-with-labels","title":"HUD with Labels","text":"
    class GameHUD : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::ui::UILabel* scoreLabel;\n    pixelroot32::graphics::ui::UILabel* livesLabel;\n\npublic:\n    GameHUD()\n        : Entity(0, 0, 240, 240, pixelroot32::core::EntityType::UI_ELEMENT) {\n        setRenderLayer(2);\n\n        scoreLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Score: 0\", 10, 10,\n            pixelroot32::graphics::Color::White, 1\n        );\n\n        livesLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Lives: 3\", 10, 20,\n            pixelroot32::graphics::Color::White, 1\n        );\n    }\n\n    void updateHUD(int score, int lives) {\n        char buffer[32];\n        snprintf(buffer, sizeof(buffer), \"Score: %d\", score);\n        scoreLabel->setText(buffer);\n\n        snprintf(buffer, sizeof(buffer), \"Lives: %d\", lives);\n        livesLabel->setText(buffer);\n    }\n};\n
    "},{"location":"reference/code_examples/#physics","title":"Physics","text":""},{"location":"reference/code_examples/#bouncing-ball","title":"Bouncing Ball","text":"
    class BouncingBall : public pixelroot32::core::PhysicsActor {\npublic:\n    BouncingBall(float x, float y, float radius)\n        : PhysicsActor(x, y, radius * 2, radius * 2) {\n        setRenderLayer(1);\n        setRestitution(0.9f);\n        setFriction(0.05f);\n        setWorldSize(240, 240);\n        setVelocity(50.0f, -30.0f);\n    }\n\n    void update(unsigned long deltaTime) override {\n        float gravity = 200.0f;\n        float dt = deltaTime * 0.001f;\n        setVelocity(vx, vy + gravity * dt);\n        PhysicsActor::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        int radius = width / 2;\n        renderer.drawFilledCircle(\n            static_cast<int>(x + radius),\n            static_cast<int>(y + radius),\n            radius,\n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n};\n
    "},{"location":"reference/code_examples/#platformer-player","title":"Platformer Player","text":"
    class PlatformerPlayer : public pixelroot32::core::PhysicsActor {\nprivate:\n    bool canJump = true;\n    float jumpForce = 250.0f;\n    float moveSpeed = 100.0f;\n\npublic:\n    PlatformerPlayer(float x, float y)\n        : PhysicsActor(x, y, 16, 16) {\n        setRenderLayer(1);\n        setFriction(0.3f);\n        setWorldSize(240, 240);\n    }\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        float dt = deltaTime * 0.001f;\n\n        // Horizontal movement\n        float moveDir = 0.0f;\n        if (input.isButtonDown(Buttons::LEFT)) moveDir -= 1.0f;\n        if (input.isButtonDown(Buttons::RIGHT)) moveDir += 1.0f;\n        setVelocity(moveDir * moveSpeed, vy);\n\n        // Gravity\n        float gravity = 300.0f;\n        setVelocity(vx, vy + gravity * dt);\n\n        // Jump\n        if (input.isButtonPressed(Buttons::A) && canJump) {\n            setVelocity(vx, -jumpForce);\n            canJump = false;\n        }\n\n        PhysicsActor::update(deltaTime);\n\n        // Check if on ground\n        auto collisionInfo = getWorldCollisionInfo();\n        if (collisionInfo.bottom) {\n            canJump = true;\n        }\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n};\n
    "},{"location":"reference/code_examples/#sprites-and-animation","title":"Sprites and Animation","text":""},{"location":"reference/code_examples/#simple-sprite","title":"Simple Sprite","text":"
    // Define sprite data\nstatic const uint16_t PLAYER_SPRITE_DATA[] = {\n    0b00111100,\n    0b01111110,\n    0b11111111,\n    0b11111111,\n    0b11111111,\n    0b01111110,\n    0b00111100,\n    0b00000000\n};\n\nstatic const pixelroot32::graphics::Sprite PLAYER_SPRITE = {\n    PLAYER_SPRITE_DATA, 8, 8\n};\n\n// Draw sprite\nrenderer.drawSprite(PLAYER_SPRITE, 100, 100, \n    pixelroot32::graphics::Color::White);\n
    "},{"location":"reference/code_examples/#sprite-animation","title":"Sprite Animation","text":"
    class AnimatedActor : public pixelroot32::core::Actor {\nprivate:\n    pixelroot32::graphics::SpriteAnimation animation;\n    unsigned long timer = 0;\n    const unsigned long FRAME_DURATION_MS = 100;\n\npublic:\n    AnimatedActor(float x, float y)\n        : Actor(x, y, 8, 8) {\n        setRenderLayer(1);\n        animation.frames = WALK_ANIMATION_FRAMES;\n        animation.frameCount = 3;\n        animation.current = 0;\n    }\n\n    void update(unsigned long deltaTime) override {\n        timer += deltaTime;\n        if (timer >= FRAME_DURATION_MS) {\n            timer -= FRAME_DURATION_MS;\n            animation.step();\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        const auto* frame = animation.frames[animation.current].sprite;\n        renderer.drawSprite(*frame, static_cast<int>(x), static_cast<int>(y),\n            pixelroot32::graphics::Color::White);\n    }\n};\n
    "},{"location":"reference/code_examples/#camera-and-scrolling","title":"Camera and Scrolling","text":""},{"location":"reference/code_examples/#basic-camera-follow","title":"Basic Camera Follow","text":"
    class ScrollingScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    PlayerActor* player;\n\npublic:\n    void init() override {\n        int screenWidth = engine.getRenderer().getWidth();\n        int screenHeight = engine.getRenderer().getHeight();\n\n        camera = pixelroot32::graphics::Camera2D(screenWidth, screenHeight);\n        camera.setBounds(0, 2000 - screenWidth);\n\n        player = new PlayerActor(100, 100);\n        addEntity(player);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n        camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        camera.apply(renderer);\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"reference/code_examples/#tilemaps","title":"Tilemaps","text":""},{"location":"reference/code_examples/#simple-tilemap","title":"Simple Tilemap","text":"
    // Define tiles\nstatic const uint16_t TILE_EMPTY_BITS[] = { /* ... */ };\nstatic const uint16_t TILE_GROUND_BITS[] = { /* ... */ };\n\nstatic const pixelroot32::graphics::Sprite TILES[] = {\n    { TILE_EMPTY_BITS, 8, 8 },\n    { TILE_GROUND_BITS, 8, 8 }\n};\n\n// Create tilemap\nstatic uint8_t TILEMAP_INDICES[30 * 20];\nstatic pixelroot32::graphics::TileMap levelTileMap = {\n    TILEMAP_INDICES, 30, 20, TILES, 8, 8, 2\n};\n\n// Initialize\nvoid initTilemap() {\n    for (int i = 0; i < 30 * 20; i++) {\n        TILEMAP_INDICES[i] = 0;\n    }\n    // Set ground row\n    for (int x = 0; x < 30; x++) {\n        TILEMAP_INDICES[19 * 30 + x] = 1; // Ground tile\n    }\n}\n\n// Draw\nrenderer.drawTileMap(levelTileMap, 0, 0, \n    pixelroot32::graphics::Color::White);\n
    "},{"location":"reference/code_examples/#particles","title":"Particles","text":""},{"location":"reference/code_examples/#explosion-effect","title":"Explosion Effect","text":"
    #include <graphics/particles/ParticleEmitter.h>\n#include <graphics/particles/ParticlePresets.h>\n\nclass ExplosionEffect : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::particles::ParticleEmitter* emitter;\n\npublic:\n    ExplosionEffect()\n        : Entity(0, 0, 1, 1, pixelroot32::core::EntityType::GENERIC) {\n        setRenderLayer(1);\n        emitter = new pixelroot32::graphics::particles::ParticleEmitter(\n            0, 0,\n            pixelroot32::graphics::particles::ParticlePresets::Explosion()\n        );\n    }\n\n    void trigger(float x, float y) {\n        this->x = x;\n        this->y = y;\n        emitter->burst(x, y, 25);\n    }\n\n    void update(unsigned long deltaTime) override {\n        emitter->update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        emitter->draw(renderer);\n    }\n};\n
    "},{"location":"reference/code_examples/#object-pooling","title":"Object Pooling","text":""},{"location":"reference/code_examples/#entity-pool","title":"Entity Pool","text":"
    template<typename T, int POOL_SIZE>\nclass EntityPool {\nprivate:\n    T pool[POOL_SIZE];\n    bool inUse[POOL_SIZE];\n    int activeCount = 0;\n\npublic:\n    EntityPool() {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            inUse[i] = false;\n        }\n    }\n\n    T* acquire() {\n        if (activeCount >= POOL_SIZE) return nullptr;\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (!inUse[i]) {\n                inUse[i] = true;\n                activeCount++;\n                return &pool[i];\n            }\n        }\n        return nullptr;\n    }\n\n    void release(T* obj) {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (&pool[i] == obj) {\n                inUse[i] = false;\n                activeCount--;\n                obj->isEnabled = false;\n                obj->isVisible = false;\n                break;\n            }\n        }\n    }\n};\n
    "},{"location":"reference/code_examples/#common-patterns","title":"Common Patterns","text":""},{"location":"reference/code_examples/#state-machine","title":"State Machine","text":"
    enum class GameState {\n    MENU,\n    PLAYING,\n    PAUSED,\n    GAME_OVER\n};\n\nclass GameScene : public pixelroot32::core::Scene {\nprivate:\n    GameState currentState = GameState::MENU;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        switch (currentState) {\n            case GameState::MENU:\n                updateMenu();\n                break;\n            case GameState::PLAYING:\n                updateGame();\n                break;\n            case GameState::PAUSED:\n                updatePause();\n                break;\n            case GameState::GAME_OVER:\n                updateGameOver();\n                break;\n        }\n        Scene::update(deltaTime);\n    }\n};\n
    "},{"location":"reference/code_examples/#timer-pattern","title":"Timer Pattern","text":"
    class Timer {\nprivate:\n    unsigned long duration;\n    unsigned long elapsed = 0;\n    bool active = false;\n\npublic:\n    Timer(unsigned long ms) : duration(ms) {}\n\n    void start() {\n        active = true;\n        elapsed = 0;\n    }\n\n    void update(unsigned long deltaTime) {\n        if (active) {\n            elapsed += deltaTime;\n            if (elapsed >= duration) {\n                active = false;\n            }\n        }\n    }\n\n    bool isFinished() const { return !active && elapsed >= duration; }\n    bool isActive() const { return active; }\n    float getProgress() const { return static_cast<float>(elapsed) / duration; }\n};\n
    "},{"location":"reference/code_examples/#see-also","title":"See Also","text":"
    • API Reference Overview - Complete API documentation
    • Game Examples Guide - Learn from complete games
    • Manual - Game Development - Detailed guides
    "},{"location":"reference/game_examples_guide/","title":"Game Examples Guide","text":"

    This guide analyzes the complete game examples included with PixelRoot32, explaining their architecture, patterns, and lessons learned.

    "},{"location":"reference/game_examples_guide/#available-examples","title":"Available Examples","text":"

    PixelRoot32 includes several complete game examples:

    • Space Invaders: Full-featured shooter with enemies, projectiles, and audio
    • Pong: Classic arcade game with physics and collisions
    • Snake: Grid-based game with entity pooling
    • TicTacToe: Turn-based game with simple AI
    • CameraDemo: Platformer with scrolling camera and parallax
    • SpritesDemo: Demonstration of advanced sprite formats
    "},{"location":"reference/game_examples_guide/#space-invaders","title":"Space Invaders","text":"

    Location: src/examples/SpaceInvaders/

    "},{"location":"reference/game_examples_guide/#architecture","title":"Architecture","text":"

    Space Invaders demonstrates a complete game with multiple systems:

    • Scene Management: SpaceInvadersScene manages game state
    • Actor Hierarchy: PlayerActor, AlienActor, ProjectileActor, BunkerActor
    • Collision System: Uses collision layers for player, enemies, projectiles
    • Audio Integration: Sound effects for shooting, explosions, music
    • Tilemap Background: Starfield using tilemap system
    "},{"location":"reference/game_examples_guide/#key-systems","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#collision-layers","title":"Collision Layers","text":"
    namespace Layers {\n    constexpr uint16_t PLAYER = 0x0001;\n    constexpr uint16_t ALIEN = 0x0002;\n    constexpr uint16_t PROJECTILE = 0x0004;\n    constexpr uint16_t BUNKER = 0x0008;\n}\n\n// Player can collide with aliens and bunkers\nplayer->setCollisionLayer(Layers::PLAYER);\nplayer->setCollisionMask(Layers::ALIEN | Layers::BUNKER);\n\n// Projectiles can hit aliens and bunkers\nprojectile->setCollisionLayer(Layers::PROJECTILE);\nprojectile->setCollisionMask(Layers::ALIEN | Layers::BUNKER);\n
    "},{"location":"reference/game_examples_guide/#entity-management","title":"Entity Management","text":"
    • Uses object pooling for projectiles
    • Manages alien formation with grid layout
    • Handles game state (playing, game over)
    "},{"location":"reference/game_examples_guide/#audio-integration","title":"Audio Integration","text":"
    • Background music using MusicPlayer
    • Sound effects for various events
    • Audio events triggered on collisions
    "},{"location":"reference/game_examples_guide/#patterns-used","title":"Patterns Used","text":"
    • Object Pooling: Projectiles are pooled and reused
    • State Machine: Game states (playing, game over, victory)
    • Grid Layout: Alien formation uses grid-based positioning
    • Event-Driven Audio: Sounds triggered by game events
    "},{"location":"reference/game_examples_guide/#lessons-learned","title":"Lessons Learned","text":"
    • Collision layers are essential for complex games
    • Object pooling improves performance
    • Tilemaps are efficient for backgrounds
    • Audio enhances game feel significantly
    "},{"location":"reference/game_examples_guide/#pong","title":"Pong","text":"

    Location: src/examples/Pong/

    "},{"location":"reference/game_examples_guide/#architecture_1","title":"Architecture","text":"

    Pong demonstrates physics and collision handling:

    • PhysicsActor: Ball uses PhysicsActor for automatic physics
    • Collision Callbacks: Paddles and ball handle collisions
    • Score System: Simple score tracking and display
    • Game State: Reset and game over handling
    "},{"location":"reference/game_examples_guide/#key-systems_1","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#physics-setup","title":"Physics Setup","text":"
    class BallActor : public pixelroot32::core::PhysicsActor {\npublic:\n    BallActor(float x, float y, float speed, int radius)\n        : PhysicsActor(x, y, radius * 2, radius * 2) {\n        setRestitution(0.8f);  // Bouncy\n        setFriction(0.1f);     // Low friction\n        setWorldSize(240, 240);\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#collision-response","title":"Collision Response","text":"
    void BallActor::onCollision(pixelroot32::core::Actor* other) {\n    // Adjust ball position\n    // Modify velocity based on impact point\n    // Play bounce sound\n}\n
    "},{"location":"reference/game_examples_guide/#patterns-used_1","title":"Patterns Used","text":"
    • Physics Integration: Uses PhysicsActor for automatic movement
    • Collision Response: Custom collision handling
    • Score Management: Simple state tracking
    • Audio Feedback: Sound on collision
    "},{"location":"reference/game_examples_guide/#lessons-learned_1","title":"Lessons Learned","text":"
    • PhysicsActor simplifies physics-based games
    • Collision callbacks allow custom response logic
    • Simple games can be very effective
    "},{"location":"reference/game_examples_guide/#snake","title":"Snake","text":"

    Location: src/examples/Snake/

    "},{"location":"reference/game_examples_guide/#architecture_2","title":"Architecture","text":"

    Snake demonstrates entity pooling and grid-based movement:

    • Entity Pooling: Snake segments are pooled
    • Grid Movement: Movement constrained to grid
    • Game Logic: Food spawning, collision detection
    • State Management: Game over, reset functionality
    "},{"location":"reference/game_examples_guide/#key-systems_2","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#entity-pooling","title":"Entity Pooling","text":"
    class SnakeScene {\nprivate:\n    std::vector<SnakeSegmentActor*> segmentPool;\n    std::vector<SnakeSegmentActor*> snakeSegments;\n\n    void resetGame() {\n        // Reuse pooled segments\n        for (int i = 0; i < initialLength; ++i) {\n            SnakeSegmentActor* segment = segmentPool[i];\n            segment->resetAlive();\n            snakeSegments.push_back(segment);\n            addEntity(segment);\n        }\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#grid-based-movement","title":"Grid-Based Movement","text":"
    class SnakeSegmentActor : public pixelroot32::core::Actor {\nprivate:\n    int cellX, cellY;  // Grid position\n\npublic:\n    void setCellPosition(int x, int y) {\n        cellX = x;\n        cellY = y;\n        // Convert to world position\n        this->x = cellX * CELL_SIZE;\n        this->y = cellY * CELL_SIZE;\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#patterns-used_2","title":"Patterns Used","text":"
    • Object Pooling: Segments are pre-allocated and reused
    • Grid System: Discrete grid-based movement
    • Linked List: Snake segments form a linked structure
    • Food Spawning: Random food placement with collision checking
    "},{"location":"reference/game_examples_guide/#lessons-learned_2","title":"Lessons Learned","text":"
    • Entity pooling is essential for dynamic entities
    • Grid-based movement simplifies collision detection
    • Pre-allocation avoids memory fragmentation
    "},{"location":"reference/game_examples_guide/#tictactoe","title":"TicTacToe","text":"

    Location: src/examples/TicTacToe/

    "},{"location":"reference/game_examples_guide/#architecture_3","title":"Architecture","text":"

    TicTacToe demonstrates turn-based logic and simple AI:

    • Turn Management: Player vs AI turns
    • Game Board: 3x3 grid representation
    • Win Detection: Check for winning conditions
    • Simple AI: Random move selection
    "},{"location":"reference/game_examples_guide/#key-systems_3","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#board-representation","title":"Board Representation","text":"
    class TicTacToeScene {\nprivate:\n    int board[3][3];  // 0=empty, 1=X, 2=O\n    bool playerTurn = true;\n\n    bool makeMove(int row, int col, int player) {\n        if (board[row][col] == 0) {\n            board[row][col] = player;\n            return true;\n        }\n        return false;\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#win-detection","title":"Win Detection","text":"
    int checkWinner() {\n    // Check rows\n    for (int i = 0; i < 3; i++) {\n        if (board[i][0] == board[i][1] && board[i][1] == board[i][2]) {\n            return board[i][0];\n        }\n    }\n    // Check columns, diagonals...\n    return 0; // No winner\n}\n
    "},{"location":"reference/game_examples_guide/#patterns-used_3","title":"Patterns Used","text":"
    • State Machine: Turn-based state management
    • Grid Logic: 2D array for board representation
    • Simple AI: Random valid move selection
    • UI Integration: Buttons for player input
    "},{"location":"reference/game_examples_guide/#lessons-learned_3","title":"Lessons Learned","text":"
    • Turn-based games are straightforward to implement
    • Simple AI can be effective for basic games
    • Grid-based logic is easy to reason about
    "},{"location":"reference/game_examples_guide/#camerademo","title":"CameraDemo","text":"

    Location: src/examples/CameraDemo/

    "},{"location":"reference/game_examples_guide/#architecture_4","title":"Architecture","text":"

    CameraDemo demonstrates scrolling and parallax:

    • Camera2D: Camera following player
    • Tilemap: Level built with tilemap
    • Parallax: Multiple background layers
    • Platformer Physics: Player with jumping and gravity
    "},{"location":"reference/game_examples_guide/#key-systems_4","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#camera-setup","title":"Camera Setup","text":"
    class CameraDemoScene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    float levelWidth;\n\npublic:\n    void init() override {\n        camera = pixelroot32::graphics::Camera2D(240, 240);\n        camera.setBounds(0, levelWidth - 240);\n    }\n\n    void update(unsigned long deltaTime) override {\n        camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        camera.apply(renderer);\n        renderer.drawTileMap(levelTileMap, 0, 0, Color::White);\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#platformer-physics","title":"Platformer Physics","text":"
    class PlayerCube : public pixelroot32::core::PhysicsActor {\npublic:\n    void update(unsigned long deltaTime) override {\n        // Input handling\n        // Gravity application\n        // Jump logic\n        // Platform collision\n        PhysicsActor::update(deltaTime);\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#patterns-used_4","title":"Patterns Used","text":"
    • Camera Following: Dead-zone camera following
    • Tilemap Rendering: Efficient level rendering
    • Parallax Scrolling: Multiple background layers
    • Platform Collision: Custom collision with platforms
    "},{"location":"reference/game_examples_guide/#lessons-learned_4","title":"Lessons Learned","text":"
    • Camera system enables large levels
    • Tilemaps are efficient for level data
    • Parallax adds depth to 2D games
    • Platform collision requires custom logic
    "},{"location":"reference/game_examples_guide/#spritesdemo","title":"SpritesDemo","text":"

    Location: src/examples/SpritesDemo/

    "},{"location":"reference/game_examples_guide/#architecture_5","title":"Architecture","text":"

    SpritesDemo showcases advanced sprite formats:

    • 2bpp Sprites: 4-color sprite format
    • 4bpp Sprites: 16-color sprite format (if enabled)
    • Animation: Sprite animation examples
    • Format Comparison: Side-by-side format display
    "},{"location":"reference/game_examples_guide/#key-systems_5","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#2bpp-sprite-usage","title":"2bpp Sprite Usage","text":"
    #ifdef PIXELROOT32_ENABLE_2BPP_SPRITES\nstatic const pixelroot32::graphics::Sprite2bpp SPRITE_2BPP = {\n    SPRITE_DATA,\n    SPRITE_PALETTE,\n    16, 32, 4\n};\n\nrenderer.drawSprite(SPRITE_2BPP, x, y, false);\n#endif\n
    "},{"location":"reference/game_examples_guide/#animation-display","title":"Animation Display","text":"
    class SpritesDemoActor : public pixelroot32::core::Entity {\nprivate:\n    unsigned long timer = 0;\n    uint8_t currentFrame = 0;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        timer += deltaTime;\n        if (timer >= 150) {\n            timer -= 150;\n            currentFrame = (currentFrame + 1) % 9;\n        }\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#patterns-used_5","title":"Patterns Used","text":"
    • Format Comparison: Shows different sprite formats
    • Animation Loop: Frame-based animation
    • Conditional Compilation: Uses build flags
    "},{"location":"reference/game_examples_guide/#lessons-learned_5","title":"Lessons Learned","text":"
    • Advanced formats provide more color options
    • Animation is straightforward with frame arrays
    • Build flags enable/disable experimental features
    "},{"location":"reference/game_examples_guide/#common-patterns-across-examples","title":"Common Patterns Across Examples","text":""},{"location":"reference/game_examples_guide/#scene-initialization","title":"Scene Initialization","text":"

    All examples follow this pattern:

    void init() override {\n    // 1. Set palette\n    pixelroot32::graphics::setPalette(PaletteType::NES);\n\n    // 2. Create background entity\n    addEntity(new BackgroundEntity());\n\n    // 3. Create game entities\n    player = new PlayerActor(...);\n    addEntity(player);\n\n    // 4. Initialize game state\n    resetGame();\n}\n
    "},{"location":"reference/game_examples_guide/#update-pattern","title":"Update Pattern","text":"
    void update(unsigned long deltaTime) override {\n    // 1. Process input\n    handleInput();\n\n    // 2. Update game logic\n    updateGameLogic();\n\n    // 3. Call parent update (updates all entities)\n    Scene::update(deltaTime);\n\n    // 4. Post-update logic\n    checkGameState();\n}\n
    "},{"location":"reference/game_examples_guide/#draw-pattern","title":"Draw Pattern","text":"
    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // 1. Apply camera (if used)\n    camera.apply(renderer);\n\n    // 2. Draw background/tilemap\n    renderer.drawTileMap(background, 0, 0, Color::White);\n\n    // 3. Call parent draw (draws all entities)\n    Scene::draw(renderer);\n\n    // 4. Draw UI/HUD\n    drawHUD(renderer);\n}\n
    "},{"location":"reference/game_examples_guide/#learning-path","title":"Learning Path","text":""},{"location":"reference/game_examples_guide/#beginner-examples","title":"Beginner Examples","text":"
    1. Pong: Start here - simple physics and collisions
    2. TicTacToe: Learn turn-based logic
    3. Snake: Understand entity pooling
    "},{"location":"reference/game_examples_guide/#intermediate-examples","title":"Intermediate Examples","text":"
    1. CameraDemo: Learn scrolling and parallax
    2. SpritesDemo: Explore sprite formats
    "},{"location":"reference/game_examples_guide/#advanced-examples","title":"Advanced Examples","text":"
    1. Space Invaders: Complete game with all systems
    "},{"location":"reference/game_examples_guide/#code-study-recommendations","title":"Code Study Recommendations","text":""},{"location":"reference/game_examples_guide/#for-learning-physics","title":"For Learning Physics","text":"
    • Study Pong/BallActor.cpp - PhysicsActor usage
    • Study CameraDemo/PlayerCube.cpp - Platformer physics
    "},{"location":"reference/game_examples_guide/#for-learning-collisions","title":"For Learning Collisions","text":"
    • Study SpaceInvaders - Complex collision layers
    • Study Pong - Simple collision response
    "},{"location":"reference/game_examples_guide/#for-learning-memory-management","title":"For Learning Memory Management","text":"
    • Study Snake/SnakeScene.cpp - Entity pooling
    • Study SpaceInvaders - Projectile pooling
    "},{"location":"reference/game_examples_guide/#for-learning-audio","title":"For Learning Audio","text":"
    • Study SpaceInvaders - Music and sound effects
    • Study Pong - Simple audio integration
    "},{"location":"reference/game_examples_guide/#for-learning-ui","title":"For Learning UI","text":"
    • Study TicTacToe - Button-based UI
    • Study menu scenes - Layout usage
    "},{"location":"reference/game_examples_guide/#extending-examples","title":"Extending Examples","text":""},{"location":"reference/game_examples_guide/#adding-features","title":"Adding Features","text":"
    • Pong: Add power-ups, multiple balls
    • Snake: Add obstacles, multiple food types
    • Space Invaders: Add boss battles, power-ups
    "},{"location":"reference/game_examples_guide/#creating-variations","title":"Creating Variations","text":"
    • Pong: Make it vertical, add walls
    • Snake: Change to hexagonal grid
    • TicTacToe: Make it 4x4 or 5x5
    "},{"location":"reference/game_examples_guide/#best-practices-from-examples","title":"Best Practices from Examples","text":"
    1. Pre-allocate Resources: All examples pre-allocate entities
    2. Use Object Pooling: For frequently created/destroyed entities
    3. Organize by Layers: Clear collision layer organization
    4. Separate Concerns: Game logic separate from rendering
    5. State Management: Clear game state handling
    "},{"location":"reference/game_examples_guide/#see-also","title":"See Also","text":"
    • Code Examples - Reusable code snippets
    • API Reference Overview - Complete API documentation
    • Manual - Game Development - Detailed guides

    Note: All example code is available in the src/examples/ directory of the PixelRoot32 Game Samples project.

    "},{"location":"resources/available_tools/","title":"Available Tools","text":"

    This guide documents tools available to facilitate PixelRoot32 game development.

    "},{"location":"resources/available_tools/#sprite-compiler-pr32-sprite-compiler","title":"Sprite Compiler (pr32-sprite-compiler)","text":"

    The Sprite Compiler converts PNG images to PixelRoot32 sprite data formats, making it easy to create sprites from image files.

    Read more in the Sprite Compiler Guide

    "},{"location":"resources/available_tools/#tilemap-editor","title":"Tilemap Editor","text":"

    The Tilemap Editor is a visual tool for designing multi-layered tile-based environments. It allows you to import tilesets, paint maps, and export optimized C++ code directly for use with PixelRoot32.

    Read more in the Tilemap Editor Guide

    "},{"location":"resources/available_tools/#key-features","title":"Key Features","text":"
    • Multi-layer editing: Up to 8 layers with transparency support.
    • BPP support: Export maps in 1bpp, 2bpp, or 4bpp formats.
    • Visual Tools: Brush, Eraser, Rectangle Fill, and Pipette.
    • Standalone App: Available as a portable .exe for Windows.
    "},{"location":"resources/available_tools/#installation-quick-start","title":"Installation (Quick Start)","text":"

    From Source:

    git clone https://github.com/Gperez88/PixelRoot32-Tilemap-Editor.git\ncd PixelRoot32-Tilemap-Editor\npip install ttkbootstrap pillow jinja2\npython main.py\n

    From Source:

    git clone https://github.com/Gperez88/pr32-sprite-compiler.git\ncd pr32-sprite-compiler\nnpm install\nnpm link  # Optional: install globally\n

    As NPM Package:

    npm install -g pr32-sprite-compiler\n

    "},{"location":"resources/available_tools/#basic-usage","title":"Basic Usage","text":"

    Command Line:

    pr32-sprite-compiler input.png output.h\n

    With Options:

    pr32-sprite-compiler input.png output.h --format 1bpp --name MY_SPRITE\n

    "},{"location":"resources/available_tools/#supported-formats","title":"Supported Formats","text":"
    • 1bpp (default): Monochrome, most memory-efficient
    • 2bpp: 4 colors per sprite (requires PIXELROOT32_ENABLE_2BPP_SPRITES)
    • 4bpp: 16 colors per sprite (requires PIXELROOT32_ENABLE_4BPP_SPRITES)
    "},{"location":"resources/available_tools/#output-format","title":"Output Format","text":"

    The compiler generates C++ header files with sprite data:

    // output.h\n#ifndef SPRITE_DATA_H\n#define SPRITE_DATA_H\n\n#include <stdint.h>\n\nstatic const uint16_t MY_SPRITE_DATA[] = {\n    0b00111100,\n    0b01111110,\n    0b11111111,\n    // ... more rows\n};\n\nstatic const pixelroot32::graphics::Sprite MY_SPRITE = {\n    MY_SPRITE_DATA,\n    8,  // width\n    8   // height\n};\n\n#endif\n
    "},{"location":"resources/available_tools/#advanced-options","title":"Advanced Options","text":"

    Batch Processing:

    pr32-sprite-compiler --batch sprites/*.png --output-dir sprites/out/\n

    Custom Palette:

    pr32-sprite-compiler input.png output.h --palette custom_palette.json\n

    Sprite Sheet:

    pr32-sprite-compiler sheet.png output.h --sheet 8x8 --count 16\n

    "},{"location":"resources/available_tools/#gui-version","title":"GUI Version","text":"

    If available, a GUI version provides visual feedback:

    • Drag and drop images
    • Preview sprite data
    • Adjust settings visually
    • Export to header files
    "},{"location":"resources/available_tools/#step-by-step-example","title":"Step-by-Step Example","text":"
    1. Create or find a PNG image (8x8, 16x16, etc.)

    2. Run the compiler:

      pr32-sprite-compiler player.png player_sprite.h --name PLAYER_SPRITE\n

    3. Include in your project:

      #include \"player_sprite.h\"\n\nvoid draw() {\n    renderer.drawSprite(PLAYER_SPRITE, 100, 100, Color::White);\n}\n

    "},{"location":"resources/available_tools/#troubleshooting","title":"Troubleshooting","text":"

    Image too large: - Sprites must be \u2264 16 pixels wide for 1bpp - Reduce image size or split into multiple sprites

    Colors not converting correctly: - Ensure image uses indexed colors - Use black/white for 1bpp - Use 4 colors for 2bpp, 16 for 4bpp

    Output file not found: - Check write permissions - Verify output path exists

    "},{"location":"resources/available_tools/#future-tools","title":"Future Tools","text":""},{"location":"resources/available_tools/#music-compiler-planned","title":"Music Compiler (Planned)","text":"

    A tool to convert music files or MIDI to PixelRoot32 MusicTrack format.

    Planned Features: - MIDI to MusicTrack conversion - Visual music editor - Instrument preset management - Export to C++ header files

    "},{"location":"resources/available_tools/#tilemap-compiler-planned","title":"Tilemap Compiler (Planned)","text":"

    A tool to create tilemaps from image files or tile editors.

    Planned Features: - Image to tilemap conversion - Tile editor integration - Export to C++ arrays - Collision data generation

    "},{"location":"resources/available_tools/#other-planned-tools","title":"Other Planned Tools","text":"
    • Save System Generator: Generate save/load code
    • Asset Packer: Bundle assets for distribution
    • Performance Profiler: Analyze game performance
    "},{"location":"resources/available_tools/#using-tools-in-development","title":"Using Tools in Development","text":""},{"location":"resources/available_tools/#workflow-integration","title":"Workflow Integration","text":"

    Typical Workflow: 1. Create/edit sprites in image editor 2. Compile sprites to C++ headers 3. Include headers in project 4. Use sprites in code

    Automation:

    # Build script example\n#!/bin/bash\npr32-sprite-compiler assets/sprites/*.png --output-dir src/sprites/\n# Continue with build...\n

    "},{"location":"resources/available_tools/#best-practices","title":"Best Practices","text":"
    • Organize assets: Keep source images separate from generated code
    • Version control: Commit generated headers, not source images (or both)
    • Naming conventions: Use consistent naming for sprites
    • Batch processing: Process multiple sprites at once when possible
    "},{"location":"resources/available_tools/#see-also","title":"See Also","text":"
    • Sprite Compiler Documentation - Detailed sprite compiler guide
    • Manual - Sprites and Animation - Using sprites in games
    • Troubleshooting - Common tool issues

    Note: Tool availability may vary. Check the PixelRoot32 repository for the latest tool information.

    "},{"location":"resources/faq/","title":"Frequently Asked Questions","text":"

    Common questions about PixelRoot32, organized by category.

    "},{"location":"resources/faq/#general","title":"General","text":""},{"location":"resources/faq/#what-is-pixelroot32","title":"What is PixelRoot32?","text":"

    PixelRoot32 is a lightweight, modular 2D game engine designed for ESP32 microcontrollers. It provides a complete game development framework with rendering, audio, physics, input, and UI systems, optimized for limited hardware resources.

    "},{"location":"resources/faq/#what-platforms-does-it-support","title":"What platforms does it support?","text":"
    • ESP32: Primary platform (TFT displays, GPIO buttons, DAC/I2S audio)
    • Native/Desktop: Development platform (SDL2, keyboard, SDL2 audio)
    "},{"location":"resources/faq/#what-kind-of-games-can-i-make","title":"What kind of games can I make?","text":"

    PixelRoot32 is ideal for: - Retro/arcade-style games - 2D platformers - Shooters - Puzzle games - Simple RPGs - Educational games

    See Limitations and Considerations for what's not suitable.

    "},{"location":"resources/faq/#is-it-free-to-use","title":"Is it free to use?","text":"

    Yes, PixelRoot32 is open source and licensed under the MIT License. You can use it freely for personal and commercial projects.

    "},{"location":"resources/faq/#where-can-i-find-the-source-code","title":"Where can I find the source code?","text":"
    • Engine: https://github.com/Gperez88/PixelRoot32-Game-Engine
    • Samples: https://github.com/Gperez88/PixelRoot32-Game-Samples
    • Documentation: https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Docs
    "},{"location":"resources/faq/#installation-and-configuration","title":"Installation and Configuration","text":""},{"location":"resources/faq/#how-do-i-install-pixelroot32","title":"How do I install PixelRoot32?","text":"

    See Your First Project for detailed installation instructions.

    Quick answer: 1. Install PlatformIO in VS Code 2. Create new ESP32 project 3. Add library dependency: gperez88/PixelRoot32-Game-Engine@0.2.0-dev 4. Configure hardware in platformio.ini

    "},{"location":"resources/faq/#what-version-should-i-use","title":"What version should I use?","text":"

    Use the exact version 0.2.0-dev. Do NOT use ^ or fuzzy versioning, as the API may change.

    "},{"location":"resources/faq/#how-do-i-configure-my-display","title":"How do I configure my display?","text":"

    Configure TFT_eSPI via build flags in platformio.ini. See Your First Project for examples.

    "},{"location":"resources/faq/#how-do-i-set-up-audio","title":"How do I set up audio?","text":"

    Choose an audio backend: - ESP32_DAC: Simple, one pin (GPIO 25 or 26) - ESP32_I2S: Higher quality, requires external DAC - SDL2_AudioBackend: For Native/PC development

    See Audio for details.

    "},{"location":"resources/faq/#development","title":"Development","text":""},{"location":"resources/faq/#how-do-i-create-a-scene","title":"How do I create a scene?","text":"

    Inherit from pixelroot32::core::Scene and implement init(), update(), and draw(). See Scenes and Entities.

    "},{"location":"resources/faq/#how-do-i-add-entities-to-a-scene","title":"How do I add entities to a scene?","text":"

    Create entities and call addEntity() in init(). The scene manages them automatically.

    "},{"location":"resources/faq/#whats-the-difference-between-entity-actor-and-physicsactor","title":"What's the difference between Entity, Actor, and PhysicsActor?","text":"
    • Entity: Base class, can be drawn and updated
    • Actor: Entity with collision detection
    • PhysicsActor: Actor with automatic physics (velocity, gravity, friction)

    See Scenes and Entities for details.

    "},{"location":"resources/faq/#how-do-i-handle-input","title":"How do I handle input?","text":"

    Access InputManager through the engine:

    auto& input = engine.getInputManager();\nif (input.isButtonPressed(Buttons::A)) {\n    // Handle input\n}\n

    See Input and Control.

    "},{"location":"resources/faq/#how-do-i-play-sounds","title":"How do I play sounds?","text":"

    Create an AudioEvent and play it:

    pixelroot32::audio::AudioEvent sound{};\nsound.type = pixelroot32::audio::WaveType::PULSE;\nsound.frequency = 800.0f;\nsound.duration = 0.1f;\nengine.getAudioEngine().playEvent(sound);\n

    See Audio.

    "},{"location":"resources/faq/#how-do-i-create-sprites","title":"How do I create sprites?","text":"

    Define sprite data manually or use the Sprite Compiler tool. See Sprites and Animation.

    "},{"location":"resources/faq/#can-i-use-images-instead-of-manual-sprite-data","title":"Can I use images instead of manual sprite data?","text":"

    Yes, use the Sprite Compiler tool to convert PNG images to sprite data. See Available Tools.

    "},{"location":"resources/faq/#performance","title":"Performance","text":""},{"location":"resources/faq/#why-is-my-game-running-slowly","title":"Why is my game running slowly?","text":"

    Common causes: - Too many entities (MAX_ENTITIES = 32) - Too many draw calls - Expensive calculations in update() - Memory issues

    See Performance Tuning for solutions.

    "},{"location":"resources/faq/#what-fps-should-i-target","title":"What FPS should I target?","text":"

    30-60 FPS is typical. Lower complexity games can achieve 60 FPS, more complex games may need to target 30 FPS.

    "},{"location":"resources/faq/#how-do-i-optimize-my-game","title":"How do I optimize my game?","text":"
    • Use object pooling
    • Implement viewport culling
    • Reduce entity count
    • Cache calculations
    • Use tilemaps for backgrounds

    See Performance Tuning.

    "},{"location":"resources/faq/#memory","title":"Memory","text":""},{"location":"resources/faq/#why-do-i-get-out-of-memory-errors","title":"Why do I get \"out of memory\" errors?","text":"

    ESP32 has limited RAM (~320KB). Solutions: - Use object pooling - Store data in flash (const/constexpr) - Reduce entity count - Avoid dynamic allocation

    See Memory Management.

    "},{"location":"resources/faq/#what-is-max_entities","title":"What is MAX_ENTITIES?","text":"

    MAX_ENTITIES = 32 is a hard limit per scene. This includes all entities: actors, UI elements, particles, etc.

    Solutions: - Use object pooling to reuse entities - Disable entities instead of removing - Combine multiple entities into one

    "},{"location":"resources/faq/#how-do-i-check-available-memory","title":"How do I check available memory?","text":"
    #ifdef PLATFORM_ESP32\nSerial.print(\"Free heap: \");\nSerial.println(ESP.getFreeHeap());\n#endif\n
    "},{"location":"resources/faq/#hardware","title":"Hardware","text":""},{"location":"resources/faq/#which-esp32-board-should-i-use","title":"Which ESP32 board should I use?","text":"

    Any ESP32 board works. Common choices: - ESP32-WROOM-32 - ESP32-WROVER (more RAM) - ESP32-DevKit

    "},{"location":"resources/faq/#which-display-should-i-use","title":"Which display should I use?","text":"

    Popular choices: - ST7789: 240x240, good quality - ST7735: 128x128, smaller/cheaper - ILI9341: 240x320, larger

    See Platforms and Drivers.

    "},{"location":"resources/faq/#how-many-buttons-do-i-need","title":"How many buttons do I need?","text":"

    Minimum: 4 (UP, DOWN, LEFT, RIGHT) Recommended: 6 (add A and B buttons) More buttons can be added if needed.

    "},{"location":"resources/faq/#can-i-use-analog-joysticks","title":"Can I use analog joysticks?","text":"

    Not directly supported. You can read analog pins manually and convert to digital input, but the engine expects digital buttons.

    "},{"location":"resources/faq/#troubleshooting","title":"Troubleshooting","text":""},{"location":"resources/faq/#my-display-is-blank-whats-wrong","title":"My display is blank. What's wrong?","text":"
    1. Check wiring connections
    2. Verify pin numbers in platformio.ini
    3. Lower SPI frequency
    4. Check display type matches hardware
    5. Verify power supply

    See Troubleshooting.

    "},{"location":"resources/faq/#buttons-dont-work-why","title":"Buttons don't work. Why?","text":"
    1. Check button wiring
    2. Verify pin numbers in InputConfig
    3. Check pull-up/pull-down resistors
    4. Ensure InputManager is being updated
    5. Test with isButtonDown() vs isButtonPressed()
    "},{"location":"resources/faq/#audio-is-distorted-how-do-i-fix-it","title":"Audio is distorted. How do I fix it?","text":"
    1. Lower volume levels
    2. Reduce sample rate (try 11025 Hz)
    3. Check for too many simultaneous sounds
    4. Verify hardware connections
    5. Check power supply
    "},{"location":"resources/faq/#game-crashes-randomly-whats-happening","title":"Game crashes randomly. What's happening?","text":"

    Common causes: - Out of memory - Too many entities - Infinite loops - Stack overflow - Watchdog timeout

    See Troubleshooting for debugging techniques.

    "},{"location":"resources/faq/#advanced","title":"Advanced","text":""},{"location":"resources/faq/#can-i-extend-the-engine","title":"Can I extend the engine?","text":"

    Yes, PixelRoot32 is designed to be extensible. You can: - Create custom display drivers - Create custom audio backends - Extend existing systems

    See Extensibility.

    "},{"location":"resources/faq/#can-i-use-3d-graphics","title":"Can I use 3D graphics?","text":"

    No, PixelRoot32 is 2D-only. It's designed for sprite-based 2D games.

    "},{"location":"resources/faq/#can-i-add-networking","title":"Can I add networking?","text":"

    Not currently supported. The engine focuses on single-player games.

    "},{"location":"resources/faq/#can-i-save-game-data","title":"Can I save game data?","text":"

    Not currently supported. A save system is planned for future versions.

    "},{"location":"resources/faq/#can-i-use-multiple-scenes-at-once","title":"Can I use multiple scenes at once?","text":"

    Yes, use SceneManager to push/pop scenes. This is useful for menus and pause screens.

    "},{"location":"resources/faq/#getting-help","title":"Getting Help","text":""},{"location":"resources/faq/#where-can-i-get-help","title":"Where can I get help?","text":"
    • Documentation: This documentation site
    • Examples: Study example games in the samples project
    • Discord: Community Discord server
    • GitHub: Open an issue for bugs
    "},{"location":"resources/faq/#how-do-i-report-a-bug","title":"How do I report a bug?","text":"

    Create a detailed bug report with: - Platform (ESP32 or Native) - Hardware details - Minimal reproduction code - Error messages - Expected vs actual behavior

    "},{"location":"resources/faq/#can-i-contribute","title":"Can I contribute?","text":"

    Yes! PixelRoot32 is open source. Check the main repository for contribution guidelines.

    "},{"location":"resources/faq/#see-also","title":"See Also","text":"
    • Troubleshooting - Detailed problem solving
    • Limitations and Considerations - What the engine can/can't do
    • Getting Started - Start here if you're new
    • Manual - Complete development guides

    Can't find your question? Check the Troubleshooting guide or ask on the Discord server.

    "},{"location":"resources/limitations_and_considerations/","title":"Limitations and Considerations","text":"

    This document honestly documents what PixelRoot32 can and cannot do, helping you make informed decisions about using the engine.

    "},{"location":"resources/limitations_and_considerations/#hardware-limitations-esp32","title":"Hardware Limitations (ESP32)","text":""},{"location":"resources/limitations_and_considerations/#memory-constraints","title":"Memory Constraints","text":"

    RAM: - Available: ~320KB total (varies by ESP32 model) - Heap: Limited and fragmented over time - Stack: ~8KB, avoid large stack allocations - Impact: Limits entity count, sprite data, and dynamic allocation

    Flash: - Available: 4MB+ (varies by model) - Usage: Program code, sprite data, assets - Impact: Large games may approach limits

    Recommendations: - Use object pooling - Store data in flash (const/constexpr) - Avoid dynamic allocation in game loop - Keep entity count low

    "},{"location":"resources/limitations_and_considerations/#cpu-limitations","title":"CPU Limitations","text":"

    Performance: - Clock Speed: 240MHz (typically) - Single-threaded: One core handles everything - Target FPS: 30-60 FPS (depends on complexity) - Frame Budget: ~16-33ms per frame at 60 FPS

    Impact: - Complex games may struggle - Many entities reduce performance - Expensive calculations hurt FPS

    Recommendations: - Optimize rendering - Reduce entity count - Cache calculations - Profile on hardware

    "},{"location":"resources/limitations_and_considerations/#display-limitations","title":"Display Limitations","text":"

    Supported Displays: - TFT displays via SPI (ST7735, ST7789, ILI9341, etc.) - Limited to SPI displays - Resolution typically 128x128 to 320x240

    Constraints: - SPI communication speed limits - Display initialization complexity - Power consumption

    "},{"location":"resources/limitations_and_considerations/#audio-limitations","title":"Audio Limitations","text":"

    Hardware: - Internal DAC: Lower quality, simple setup - I2S: Higher quality, requires external DAC - Sample rates: 11025 Hz (DAC) or 22050 Hz (I2S)

    Constraints: - 4 channels total (2 Pulse, 1 Triangle, 1 Noise) - Music uses one channel - Limited simultaneous sounds - Quality limited by hardware

    "},{"location":"resources/limitations_and_considerations/#software-limitations","title":"Software Limitations","text":""},{"location":"resources/limitations_and_considerations/#entity-system","title":"Entity System","text":"

    MAX_ENTITIES = 32 per scene - Hard limit, cannot be changed easily - Applies to all entities (actors, UI, particles, etc.) - Must manage entity count carefully

    Workarounds: - Use object pooling - Reuse entities - Disable entities instead of removing - Combine multiple entities into one

    "},{"location":"resources/limitations_and_considerations/#no-rtti-runtime-type-information","title":"No RTTI (Runtime Type Information)","text":"

    Impact: - Cannot use dynamic_cast in most code - Type checking must be done manually - Limits polymorphism patterns

    Alternatives: - Use virtual functions - Manual type checking - Tag-based systems

    "},{"location":"resources/limitations_and_considerations/#no-exceptions-in-critical-code","title":"No Exceptions in Critical Code","text":"

    Impact: - Cannot use try/catch in game loop - Error handling must be explicit - Crashes instead of exceptions

    Best Practices: - Validate inputs - Check return values - Use assertions for debugging - Handle errors explicitly

    "},{"location":"resources/limitations_and_considerations/#no-dynamic-allocation-in-game-loop","title":"No Dynamic Allocation in Game Loop","text":"

    Impact: - Cannot use new/delete during gameplay - Must pre-allocate resources - Limits flexibility

    Solutions: - Object pooling - Pre-allocation in init() - Static buffers - Fixed-size arrays

    "},{"location":"resources/limitations_and_considerations/#no-advanced-features","title":"No Advanced Features","text":"

    Not Supported: - 3D graphics - Shaders - Advanced physics (joints, constraints) - Networking - File system (ESP32) - Advanced audio effects

    Focus: - 2D sprite-based games - Simple physics - Retro-style games - Embedded-friendly features

    "},{"location":"resources/limitations_and_considerations/#experimental-features","title":"Experimental Features","text":""},{"location":"resources/limitations_and_considerations/#2bpp-sprites","title":"2bpp Sprites","text":"

    Status: Experimental - Requires PIXELROOT32_ENABLE_2BPP_SPRITES flag - May have bugs or limitations - Not fully tested

    Use with caution: - Test thoroughly - May change in future versions - Report issues if found

    "},{"location":"resources/limitations_and_considerations/#4bpp-sprites","title":"4bpp Sprites","text":"

    Status: Experimental - Requires PIXELROOT32_ENABLE_4BPP_SPRITES flag - More experimental than 2bpp - Higher memory usage

    Use with caution: - Test extensively - Monitor memory usage - May be unstable

    "},{"location":"resources/limitations_and_considerations/#scene-arena","title":"Scene Arena","text":"

    Status: Experimental - Requires PIXELROOT32_ENABLE_SCENE_ARENA flag - Alternative memory management - May have bugs

    Recommendations: - Use object pooling instead (more stable) - Test thoroughly if using - May be removed or changed

    "},{"location":"resources/limitations_and_considerations/#unsupported-features-current","title":"Unsupported Features (Current)","text":""},{"location":"resources/limitations_and_considerations/#planned-but-not-available","title":"Planned but Not Available","text":"
    • u8g2 Driver: Alternative display driver (planned)
    • Music Compiler: Tool to convert music files (planned)
    • Tilemap Compiler: Tool to create tilemaps (planned)
    • Save System: Persistent storage system (planned)
    • Spatial Partitioning: Advanced collision optimization (planned)
    "},{"location":"resources/limitations_and_considerations/#not-planned","title":"Not Planned","text":"
    • 3D Graphics: 2D-only engine
    • Networking: No network support
    • File System: No file I/O on ESP32
    • Advanced Audio: NES-like audio only
    • Scripting: No Lua/JavaScript support
    "},{"location":"resources/limitations_and_considerations/#best-practices-for-esp32","title":"Best Practices for ESP32","text":""},{"location":"resources/limitations_and_considerations/#memory-management","title":"Memory Management","text":"
    • Pre-allocate: All resources in init()
    • Object pooling: Reuse entities
    • Flash storage: Use const/constexpr for data
    • Avoid strings: Use static buffers
    • Monitor usage: Check heap regularly
    "},{"location":"resources/limitations_and_considerations/#performance","title":"Performance","text":"
    • Limit entities: Stay well below MAX_ENTITIES
    • Optimize rendering: Use culling, batching
    • Cache calculations: Avoid repeated work
    • Profile on hardware: PC performance \u2260 ESP32
    "},{"location":"resources/limitations_and_considerations/#development","title":"Development","text":"
    • Test on hardware: Don't rely only on Native
    • Start simple: Add complexity gradually
    • Monitor memory: Watch for leaks
    • Optimize incrementally: Profile and optimize
    "},{"location":"resources/limitations_and_considerations/#what-pixelroot32-is-good-for","title":"What PixelRoot32 IS Good For","text":"

    \u2705 Retro-style 2D games \u2705 Arcade games \u2705 Puzzle games \u2705 Platformers \u2705 Shooters \u2705 Educational projects \u2705 Prototyping \u2705 Embedded game development

    "},{"location":"resources/limitations_and_considerations/#what-pixelroot32-is-not-good-for","title":"What PixelRoot32 is NOT Good For","text":"

    \u274c 3D games \u274c Complex physics simulations \u274c Large open worlds \u274c Games requiring many entities \u274c Games with complex graphics \u274c Network multiplayer \u274c Games requiring file I/O

    "},{"location":"resources/limitations_and_considerations/#making-informed-decisions","title":"Making Informed Decisions","text":""},{"location":"resources/limitations_and_considerations/#before-starting-a-project","title":"Before Starting a Project","text":"
    1. Assess requirements: Does PixelRoot32 fit?
    2. Check limitations: Can you work within constraints?
    3. Plan architecture: Design around limitations
    4. Test early: Verify on hardware early
    "},{"location":"resources/limitations_and_considerations/#if-limitations-are-a-problem","title":"If Limitations Are a Problem","text":"

    Consider alternatives: - Full game engines (Unity, Godot) for complex games - Custom solutions for specific needs - Different hardware for more resources

    Or work within limits: - Simplify game design - Optimize aggressively - Use creative solutions

    "},{"location":"resources/limitations_and_considerations/#version-compatibility","title":"Version Compatibility","text":""},{"location":"resources/limitations_and_considerations/#current-version","title":"Current Version","text":"
    • Engine Version: 0.2.0-dev
    • API Stability: May change
    • Breaking Changes: Possible in future versions

    Recommendations: - Pin exact version in platformio.ini - Don't use ^ or fuzzy versioning - Test after engine updates - Review changelog

    "},{"location":"resources/limitations_and_considerations/#honest-assessment","title":"Honest Assessment","text":"

    PixelRoot32 is designed for: - Simple to medium complexity games - Retro/arcade style - ESP32 hardware constraints - Rapid development

    It is not designed for: - AAA game complexity - Modern graphics - Large-scale games - Unlimited resources

    "},{"location":"resources/limitations_and_considerations/#see-also","title":"See Also","text":"
    • Memory Management - Working with memory limits
    • Performance Tuning - Optimizing performance
    • Troubleshooting - Solving problems
    • FAQ - Common questions

    Remember: Understanding limitations helps you build better games within PixelRoot32's capabilities.

    "},{"location":"resources/troubleshooting/","title":"Troubleshooting","text":"

    This guide helps you diagnose and fix common issues when developing with PixelRoot32.

    "},{"location":"resources/troubleshooting/#compilation-problems","title":"Compilation Problems","text":""},{"location":"resources/troubleshooting/#common-compilation-errors","title":"Common Compilation Errors","text":"

    Error: Library not found

    Solution: Ensure PixelRoot32-Game-Engine is properly installed\n- Check platformio.ini has correct lib_deps\n- Verify library version matches (use exact version, not ^)\n- Try: pio lib install\n

    Error: Include file not found

    Solution: Check include paths\n- Verify lib_extra_dirs in platformio.ini\n- Check that library is in lib/ directory\n- Ensure correct namespace (pixelroot32::)\n

    Error: Undefined reference

    Solution: Link missing libraries\n- Check all required libraries are listed\n- Verify TFT_eSPI is installed for ESP32\n- Check SDL2 is installed for Native builds\n

    Error: Build flags not recognized

    Solution: Verify build flag syntax\n- Use -D FLAG_NAME (not --define)\n- Check flag names are correct\n- Ensure flags are in correct environment section\n

    "},{"location":"resources/troubleshooting/#configuration-issues","title":"Configuration Issues","text":"

    Wrong display type: - Verify DisplayType matches your hardware - Check TFT_eSPI build flags match display - Test with different display types

    Incorrect pin configuration: - Verify GPIO pins match your wiring - Check pin numbers in platformio.ini - Ensure pins aren't used by other peripherals

    "},{"location":"resources/troubleshooting/#hardware-problems-esp32","title":"Hardware Problems (ESP32)","text":""},{"location":"resources/troubleshooting/#display-not-working","title":"Display Not Working","text":"

    Symptoms: - Blank screen - Garbled display - No output

    Solutions: 1. Check wiring: - Verify SPI connections (MOSI, SCLK, DC, RST) - Check power supply (3.3V or 5V as required) - Ensure ground connections

    1. Verify configuration:
    2. Check display type matches hardware
    3. Verify pin numbers in platformio.ini
    4. Test with known working configuration

    5. SPI frequency:

    6. Lower SPI frequency (try 20MHz instead of 40MHz)
    7. Some displays need slower speeds
    8. Check display datasheet for max frequency

    9. Display initialization:

    10. Try different rotation values
    11. Check display width/height settings
    12. Verify TFT_eSPI driver is correct
    "},{"location":"resources/troubleshooting/#buttons-not-responding","title":"Buttons Not Responding","text":"

    Symptoms: - No input detected - Buttons don't trigger actions - Input feels laggy

    Solutions: 1. Check wiring: - Verify button connections to GPIO pins - Check pull-up/pull-down resistors - Test buttons with multimeter

    1. Verify pin configuration:
    2. Check InputConfig pin numbers
    3. Ensure pins match hardware
    4. Verify pins aren't used elsewhere

    5. Input debouncing:

    6. Add hardware debouncing (capacitor)
    7. Check InputManager is being updated
    8. Verify input is read in update(), not draw()

    9. Button logic:

    10. Test with isButtonDown() vs isButtonPressed()
    11. Check button indices match configuration
    12. Verify input is accessed correctly
    "},{"location":"resources/troubleshooting/#audio-not-working","title":"Audio Not Working","text":"

    Symptoms: - No sound output - Distorted audio - Audio glitches

    Solutions: 1. DAC Configuration: - Verify DAC pin (25 or 26 for ESP32) - Check sample rate (11025 Hz recommended) - Ensure audio backend is initialized

    1. I2S Configuration:
    2. Verify I2S pin connections (BCLK, LRCK, DOUT)
    3. Check external DAC is powered
    4. Verify I2S DAC is compatible

    5. Audio quality:

    6. Lower sample rate if distorted
    7. Reduce volume levels
    8. Check for too many simultaneous sounds
    9. Verify audio buffer size

    10. Hardware:

    11. Check speaker connections
    12. Verify amplifier is powered
    13. Test with different audio hardware
    14. Check audio cable connections
    "},{"location":"resources/troubleshooting/#power-issues","title":"Power Issues","text":"

    Symptoms: - ESP32 resets randomly - Display flickers - Unstable operation

    Solutions: 1. Power supply: - Use adequate power supply (500mA+ recommended) - Check voltage is stable (3.3V) - Add decoupling capacitors

    1. Current draw:
    2. Display draws significant current
    3. Audio amplifier adds load
    4. Reduce brightness if possible

    5. Wiring:

    6. Use thick wires for power
    7. Keep power wires short
    8. Add capacitors near ESP32
    "},{"location":"resources/troubleshooting/#performance-problems","title":"Performance Problems","text":""},{"location":"resources/troubleshooting/#low-fps","title":"Low FPS","text":"

    Symptoms: - Game runs slowly - Laggy movement - Stuttering

    Solutions: 1. Reduce entity count: - Limit active entities (MAX_ENTITIES = 32) - Disable off-screen entities - Use object pooling

    1. Optimize rendering:
    2. Use viewport culling
    3. Reduce draw calls
    4. Use tilemaps instead of individual sprites
    5. Limit sprite count

    6. Simplify logic:

    7. Cache expensive calculations
    8. Reduce collision checks
    9. Lower update frequency for non-critical entities

    10. Check hardware:

    11. Verify ESP32 is running at full speed (240MHz)
    12. Check for thermal throttling
    13. Ensure adequate power supply
    "},{"location":"resources/troubleshooting/#frame-drops","title":"Frame Drops","text":"

    Symptoms: - Occasional stuttering - Inconsistent frame times - Periodic freezes

    Solutions: 1. Identify bottlenecks: - Profile frame time - Check for expensive operations - Look for blocking code

    1. Optimize update loop:
    2. Avoid dynamic allocation
    3. Cache calculations
    4. Reduce string operations

    5. Memory issues:

    6. Check for memory leaks
    7. Reduce memory usage
    8. Use object pooling
    "},{"location":"resources/troubleshooting/#freezescrashes","title":"Freezes/Crashes","text":"

    Symptoms: - Game stops responding - ESP32 resets - Watchdog resets

    Solutions: 1. Memory issues: - Check available heap memory - Reduce entity count - Avoid dynamic allocation - Use object pooling

    1. Infinite loops:
    2. Check for infinite loops in update()
    3. Verify collision detection doesn't loop
    4. Check animation logic

    5. Stack overflow:

    6. Avoid large stack allocations
    7. Reduce recursion depth
    8. Move large data to heap (carefully)

    9. Watchdog:

    10. Ensure update() completes quickly
    11. Add yield() calls if needed
    12. Check for blocking operations
    "},{"location":"resources/troubleshooting/#memory-problems","title":"Memory Problems","text":""},{"location":"resources/troubleshooting/#out-of-memory","title":"Out of Memory","text":"

    Symptoms: - Compilation fails - Runtime crashes - \"Allocation failed\" errors

    Solutions: 1. Reduce memory usage: - Use 1bpp sprites instead of 2bpp/4bpp - Store data in flash (const/constexpr) - Reduce entity count - Use object pooling

    1. Optimize data:
    2. Reuse sprites
    3. Compress tilemap data
    4. Remove unused code/data

    5. Memory management:

    6. Avoid dynamic allocation in game loop
    7. Pre-allocate all resources
    8. Use static buffers
    "},{"location":"resources/troubleshooting/#memory-fragmentation","title":"Memory Fragmentation","text":"

    Symptoms: - Gradual performance degradation - Allocation failures over time - Crashes after running for a while

    Solutions: 1. Use object pooling: - Pre-allocate entities - Reuse objects instead of creating/destroying - Avoid frequent new/delete

    1. Pre-allocate resources:
    2. Allocate everything in init()
    3. Use fixed-size arrays
    4. Avoid dynamic containers
    "},{"location":"resources/troubleshooting/#native-build-problems","title":"Native Build Problems","text":""},{"location":"resources/troubleshooting/#sdl2-not-found","title":"SDL2 Not Found","text":"

    Symptoms: - Compilation fails - Linker errors - Missing SDL2 symbols

    Solutions: 1. Install SDL2: - Windows (MSYS2): pacman -S mingw-w64-x86_64-SDL2 - Linux: sudo apt-get install libsdl2-dev - macOS: brew install sdl2

    1. Check paths:
    2. Verify include paths in platformio.ini
    3. Check library paths
    4. Ensure SDL2 version is compatible

    5. Linker flags:

    6. Verify -lSDL2 is in build flags
    7. Check library search paths
    8. Ensure SDL2 DLL is accessible (Windows)
    "},{"location":"resources/troubleshooting/#window-not-opening","title":"Window Not Opening","text":"

    Symptoms: - Program runs but no window - Console shows errors - Immediate exit

    Solutions: 1. Check SDL2 initialization: - Verify SDL2 is properly initialized - Check for SDL2 error messages - Ensure display config is correct

    1. Graphics drivers:
    2. Update graphics drivers
    3. Check SDL2 video backend
    4. Test with simple SDL2 program

    5. Console output:

    6. Run from terminal to see errors
    7. Check for error messages
    8. Verify SDL2 is working
    "},{"location":"resources/troubleshooting/#debugging-techniques","title":"Debugging Techniques","text":""},{"location":"resources/troubleshooting/#serial-debugging-esp32","title":"Serial Debugging (ESP32)","text":"
    void setup() {\n    Serial.begin(115200);\n    // ... initialization\n\n    Serial.println(\"Engine initialized\");\n}\n\nvoid update(unsigned long deltaTime) override {\n    // Debug output\n    if (frameCount % 60 == 0) {\n        Serial.print(\"FPS: \");\n        Serial.println(1000.0f / deltaTime);\n    }\n    frameCount++;\n}\n
    "},{"location":"resources/troubleshooting/#memory-monitoring","title":"Memory Monitoring","text":"
    #ifdef PLATFORM_ESP32\n#include <Arduino.h>\n\nvoid checkMemory() {\n    Serial.print(\"Free heap: \");\n    Serial.println(ESP.getFreeHeap());\n    Serial.print(\"Largest block: \");\n    Serial.println(ESP.getMaxAllocHeap());\n}\n#endif\n
    "},{"location":"resources/troubleshooting/#performance-profiling","title":"Performance Profiling","text":"
    class Profiler {\nprivate:\n    unsigned long updateTime = 0;\n    unsigned long drawTime = 0;\n\npublic:\n    void startUpdate() {\n        updateTime = micros();\n    }\n\n    void endUpdate() {\n        updateTime = micros() - updateTime;\n    }\n\n    void log() {\n        Serial.print(\"Update: \");\n        Serial.print(updateTime);\n        Serial.print(\"us, Draw: \");\n        Serial.println(drawTime);\n    }\n};\n
    "},{"location":"resources/troubleshooting/#visual-debugging","title":"Visual Debugging","text":"
    • Draw hitboxes: Visualize collision boxes
    • Show FPS: Display frame rate on screen
    • Entity count: Show active entity count
    • Memory usage: Display memory statistics
    "},{"location":"resources/troubleshooting/#common-patterns-for-debugging","title":"Common Patterns for Debugging","text":""},{"location":"resources/troubleshooting/#isolate-the-problem","title":"Isolate the Problem","text":"
    1. Minimal reproduction: Create smallest code that shows issue
    2. Disable features: Turn off systems one by one
    3. Test incrementally: Add features back one at a time
    "},{"location":"resources/troubleshooting/#check-the-basics","title":"Check the Basics","text":"
    1. Verify initialization: Ensure engine.init() is called
    2. Check scene setup: Verify scene is set and initialized
    3. Test on both platforms: Compare ESP32 vs Native behavior
    4. Review recent changes: What changed before the issue?
    "},{"location":"resources/troubleshooting/#use-logging","title":"Use Logging","text":"
    #define DEBUG_MODE\n\n#ifdef DEBUG_MODE\n    #define DEBUG_LOG(x) Serial.println(x)\n#else\n    #define DEBUG_LOG(x)\n#endif\n\n// Usage\nDEBUG_LOG(\"Entity created\");\nDEBUG_LOG(\"Collision detected\");\n
    "},{"location":"resources/troubleshooting/#getting-help","title":"Getting Help","text":"

    If you can't resolve an issue:

    1. Check documentation: Review relevant guides
    2. Search examples: Look at example games
    3. Review code: Check engine source code
    4. Community: Ask on Discord or GitHub
    5. Report issue: Create detailed bug report
    "},{"location":"resources/troubleshooting/#bug-report-template","title":"Bug Report Template","text":"

    When reporting issues, include:

    • Platform: ESP32 or Native
    • Hardware: Display type, ESP32 model
    • Code: Minimal reproduction code
    • Error messages: Full error output
    • Expected behavior: What should happen
    • Actual behavior: What actually happens
    • Steps to reproduce: How to trigger the issue
    "},{"location":"resources/troubleshooting/#see-also","title":"See Also","text":"
    • Limitations and Considerations - Known limitations
    • Performance Tuning - Performance optimization
    • Memory Management - Memory optimization
    • FAQ - Frequently asked questions

    Note: Many issues are configuration-related. Double-check your setup before assuming a bug.

    "},{"location":"tools/sprite_compiler/advanced_features/","title":"Sprite Compiler Advanced Features","text":"

    Advanced features and options for the PixelRoot32 Sprite Compiler to optimize sprite conversion and handle complex scenarios.

    "},{"location":"tools/sprite_compiler/advanced_features/#dithering","title":"Dithering","text":"

    Dithering improves the visual quality of converted sprites by simulating intermediate colors through pixel patterns.

    "},{"location":"tools/sprite_compiler/advanced_features/#basic-dithering","title":"Basic Dithering","text":"

    Enable dithering for better gradient handling:

    pr32-sprite-compiler sprite.png output.h --dither\n
    "},{"location":"tools/sprite_compiler/advanced_features/#dithering-strength","title":"Dithering Strength","text":"

    Control dithering intensity:

    pr32-sprite-compiler sprite.png output.h --dither --dither-strength 0.5\n

    Values: - 0.0: No dithering - 0.5: Moderate dithering (default) - 1.0: Maximum dithering

    "},{"location":"tools/sprite_compiler/advanced_features/#dithering-algorithms","title":"Dithering Algorithms","text":"

    Choose dithering algorithm:

    # Floyd-Steinberg (default)\npr32-sprite-compiler sprite.png output.h --dither --dither-algorithm floyd-steinberg\n\n# Ordered (Bayer)\npr32-sprite-compiler sprite.png output.h --dither --dither-algorithm ordered\n\n# Atkinson\npr32-sprite-compiler sprite.png output.h --dither --dither-algorithm atkinson\n

    When to use: - Floyd-Steinberg: Best for most images, smooth gradients - Ordered: Faster, good for patterns - Atkinson: Softer, less noticeable patterns

    "},{"location":"tools/sprite_compiler/advanced_features/#layer-merging-multisprite","title":"Layer Merging (MultiSprite)","text":"

    Combine compatible sprite layers to reduce draw calls and improve performance.

    "},{"location":"tools/sprite_compiler/advanced_features/#basic-layer-merging","title":"Basic Layer Merging","text":"

    Merge layers with compatible colors:

    pr32-sprite-compiler sprite.png output.h --merge-layers\n
    "},{"location":"tools/sprite_compiler/advanced_features/#layer-compatibility","title":"Layer Compatibility","text":"

    Control which layers can be merged:

    pr32-sprite-compiler sprite.png output.h \\\n    --merge-layers \\\n    --merge-threshold 0.1\n

    Parameters: - --merge-threshold: Color similarity threshold (0.0-1.0)

    "},{"location":"tools/sprite_compiler/advanced_features/#preserve-layer-colors","title":"Preserve Layer Colors","text":"

    Keep per-layer color assignments:

    pr32-sprite-compiler sprite.png output.h \\\n    --merge-layers \\\n    --preserve-colors\n
    "},{"location":"tools/sprite_compiler/advanced_features/#bounds-packing","title":"Bounds & Packing","text":""},{"location":"tools/sprite_compiler/advanced_features/#auto-trim-transparent-borders","title":"Auto-Trim Transparent Borders","text":"

    Automatically remove transparent borders:

    pr32-sprite-compiler sprite.png output.h --trim\n

    Benefits: - Reduces sprite size - Saves memory - Faster rendering

    "},{"location":"tools/sprite_compiler/advanced_features/#manual-bounds","title":"Manual Bounds","text":"

    Specify custom bounds:

    pr32-sprite-compiler sprite.png output.h \\\n    --bounds 2,2,14,14\n

    Format: left,top,right,bottom (in pixels)

    "},{"location":"tools/sprite_compiler/advanced_features/#sprite-packing","title":"Sprite Packing","text":"

    Pack multiple sprites into pages:

    pr32-sprite-compiler --batch sprites/*.png \\\n    --output-dir packed/ \\\n    --pack \\\n    --pack-size 256x256\n

    Parameters: - --pack: Enable packing - --pack-size WxH: Page size (default: 256x256) - --pack-padding N: Padding between sprites (default: 1)

    Output: - Packed sprite sheet image - Individual sprite headers with offsets - Packing metadata

    "},{"location":"tools/sprite_compiler/advanced_features/#output-controls","title":"Output Controls","text":""},{"location":"tools/sprite_compiler/advanced_features/#endianness","title":"Endianness","text":"

    Specify byte order for multi-byte values:

    # Little-endian (default for most systems)\npr32-sprite-compiler sprite.png output.h --endian little\n\n# Big-endian\npr32-sprite-compiler sprite.png output.h --endian big\n
    "},{"location":"tools/sprite_compiler/advanced_features/#alignment","title":"Alignment","text":"

    Control data structure alignment:

    pr32-sprite-compiler sprite.png output.h --align 4\n

    Common values: - 1: No alignment (default) - 2: 2-byte alignment - 4: 4-byte alignment (recommended for ESP32) - 8: 8-byte alignment

    "},{"location":"tools/sprite_compiler/advanced_features/#output-format","title":"Output Format","text":"

    Choose output code style:

    # C-style (default)\npr32-sprite-compiler sprite.png output.h --style c\n\n# C++ style\npr32-sprite-compiler sprite.png output.h --style cpp\n\n# Minimal (no comments)\npr32-sprite-compiler sprite.png output.h --style minimal\n
    "},{"location":"tools/sprite_compiler/advanced_features/#custom-header-template","title":"Custom Header Template","text":"

    Use custom header template:

    pr32-sprite-compiler sprite.png output.h \\\n    --template custom_template.h\n

    Template variables: - {NAME}: Sprite name - {DATA}: Sprite data array - {WIDTH}: Sprite width - {HEIGHT}: Sprite height

    "},{"location":"tools/sprite_compiler/advanced_features/#metadata-generation","title":"Metadata Generation","text":""},{"location":"tools/sprite_compiler/advanced_features/#generate-metadata","title":"Generate Metadata","text":"

    Include sprite metadata:

    pr32-sprite-compiler sprite.png output.h --metadata\n

    Output includes: - Original image dimensions - Conversion parameters - Color information - Format details

    "},{"location":"tools/sprite_compiler/advanced_features/#json-metadata","title":"JSON Metadata","text":"

    Export metadata as JSON:

    pr32-sprite-compiler sprite.png output.h \\\n    --metadata \\\n    --metadata-format json \\\n    --metadata-file sprite.json\n

    JSON structure:

    {\n  \"name\": \"SPRITE\",\n  \"width\": 8,\n  \"height\": 8,\n  \"format\": \"1bpp\",\n  \"originalSize\": {\"width\": 8, \"height\": 8},\n  \"colors\": 2,\n  \"dataSize\": 8\n}\n

    "},{"location":"tools/sprite_compiler/advanced_features/#color-processing","title":"Color Processing","text":""},{"location":"tools/sprite_compiler/advanced_features/#color-quantization","title":"Color Quantization","text":"

    Reduce colors intelligently:

    pr32-sprite-compiler sprite.png output.h \\\n    --quantize \\\n    --colors 4\n

    Parameters: - --quantize: Enable quantization - --colors N: Target color count

    "},{"location":"tools/sprite_compiler/advanced_features/#color-mapping","title":"Color Mapping","text":"

    Custom color mapping:

    pr32-sprite-compiler sprite.png output.h \\\n    --color-map map.json\n

    Map JSON format:

    {\n  \"mappings\": [\n    {\"from\": [255, 0, 0], \"to\": [255, 255, 255]},\n    {\"from\": [0, 255, 0], \"to\": [0, 0, 0]}\n  ]\n}\n

    "},{"location":"tools/sprite_compiler/advanced_features/#gamma-correction","title":"Gamma Correction","text":"

    Apply gamma correction:

    pr32-sprite-compiler sprite.png output.h --gamma 2.2\n

    Common values: - 1.0: No correction - 2.2: Standard (sRGB) - 1.8: Mac standard

    "},{"location":"tools/sprite_compiler/advanced_features/#performance-optimization","title":"Performance Optimization","text":""},{"location":"tools/sprite_compiler/advanced_features/#parallel-processing","title":"Parallel Processing","text":"

    Process multiple files in parallel:

    pr32-sprite-compiler --batch sprites/*.png \\\n    --output-dir generated/ \\\n    --parallel 4\n

    Parameters: - --parallel N: Number of parallel workers (default: CPU cores)

    "},{"location":"tools/sprite_compiler/advanced_features/#caching","title":"Caching","text":"

    Enable conversion caching:

    pr32-sprite-compiler sprite.png output.h --cache\n

    Benefits: - Faster re-compilation - Skips unchanged files - Cache stored in .sprite-compiler-cache/

    "},{"location":"tools/sprite_compiler/advanced_features/#incremental-builds","title":"Incremental Builds","text":"

    Only process changed files:

    pr32-sprite-compiler --batch sprites/*.png \\\n    --output-dir generated/ \\\n    --incremental\n
    "},{"location":"tools/sprite_compiler/advanced_features/#validation","title":"Validation","text":""},{"location":"tools/sprite_compiler/advanced_features/#validate-output","title":"Validate Output","text":"

    Verify generated sprite data:

    pr32-sprite-compiler sprite.png output.h --validate\n

    Checks: - Data array correctness - Size constraints - Format compliance - Memory usage

    "},{"location":"tools/sprite_compiler/advanced_features/#preview-mode","title":"Preview Mode","text":"

    Preview conversion without generating file:

    pr32-sprite-compiler sprite.png --preview\n

    Output: - Console preview of sprite data - Statistics (size, colors, etc.) - Validation results

    "},{"location":"tools/sprite_compiler/advanced_features/#multi-format-output","title":"Multi-Format Output","text":""},{"location":"tools/sprite_compiler/advanced_features/#generate-multiple-formats","title":"Generate Multiple Formats","text":"

    Output in multiple formats simultaneously:

    pr32-sprite-compiler sprite.png output.h \\\n    --formats 1bpp,2bpp,4bpp \\\n    --output-dir formats/\n

    Outputs: - output_1bpp.h - output_2bpp.h - output_4bpp.h

    "},{"location":"tools/sprite_compiler/advanced_features/#custom-scripts","title":"Custom Scripts","text":""},{"location":"tools/sprite_compiler/advanced_features/#pre-processing-script","title":"Pre-Processing Script","text":"

    Run custom script before conversion:

    pr32-sprite-compiler sprite.png output.h \\\n    --pre-script preprocess.js\n
    "},{"location":"tools/sprite_compiler/advanced_features/#post-processing-script","title":"Post-Processing Script","text":"

    Run custom script after conversion:

    pr32-sprite-compiler sprite.png output.h \\\n    --post-script postprocess.js\n

    Script receives: - Input file path - Output file path - Conversion parameters - Metadata

    "},{"location":"tools/sprite_compiler/advanced_features/#advanced-batch-options","title":"Advanced Batch Options","text":""},{"location":"tools/sprite_compiler/advanced_features/#filter-by-size","title":"Filter by Size","text":"

    Process only sprites matching size:

    pr32-sprite-compiler --batch sprites/*.png \\\n    --output-dir generated/ \\\n    --filter-size 8x8\n
    "},{"location":"tools/sprite_compiler/advanced_features/#filter-by-format","title":"Filter by Format","text":"

    Process only specific formats:

    pr32-sprite-compiler --batch sprites/*.png \\\n    --output-dir generated/ \\\n    --filter-format indexed\n
    "},{"location":"tools/sprite_compiler/advanced_features/#exclude-patterns","title":"Exclude Patterns","text":"

    Exclude files matching pattern:

    pr32-sprite-compiler --batch sprites/*.png \\\n    --output-dir generated/ \\\n    --exclude \"*_old.png\"\n
    "},{"location":"tools/sprite_compiler/advanced_features/#integration-examples","title":"Integration Examples","text":""},{"location":"tools/sprite_compiler/advanced_features/#watch-mode","title":"Watch Mode","text":"

    Automatically recompile on file changes:

    pr32-sprite-compiler --watch assets/sprites/ \\\n    --output-dir src/sprites/\n
    "},{"location":"tools/sprite_compiler/advanced_features/#cicd-integration","title":"CI/CD Integration","text":"

    Example GitHub Actions workflow:

    name: Compile Sprites\non: [push]\njobs:\n  compile:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions/setup-node@v2\n      - run: npm install -g pr32-sprite-compiler\n      - run: pr32-sprite-compiler --batch assets/sprites/*.png --output-dir src/sprites/\n
    "},{"location":"tools/sprite_compiler/advanced_features/#best-practices","title":"Best Practices","text":""},{"location":"tools/sprite_compiler/advanced_features/#when-to-use-advanced-features","title":"When to Use Advanced Features","text":"

    Dithering: - Use for gradients and smooth transitions - Avoid for sharp, pixel-art style graphics

    Layer Merging: - Use for complex sprites with multiple layers - Test performance impact first

    Packing: - Use for large sprite collections - Consider memory vs. performance trade-off

    Trimming: - Always use for sprites with transparent borders - Saves memory and improves performance

    "},{"location":"tools/sprite_compiler/advanced_features/#performance-tips","title":"Performance Tips","text":"
    • Use --parallel for batch processing
    • Enable --cache for development
    • Use --incremental for large projects
    • Validate output with --validate
    "},{"location":"tools/sprite_compiler/advanced_features/#troubleshooting-advanced-features","title":"Troubleshooting Advanced Features","text":""},{"location":"tools/sprite_compiler/advanced_features/#dithering-issues","title":"Dithering Issues","text":"

    Too much noise: - Reduce --dither-strength - Try different algorithm - Consider manual color reduction

    "},{"location":"tools/sprite_compiler/advanced_features/#packing-issues","title":"Packing Issues","text":"

    Sprites don't fit: - Increase --pack-size - Reduce sprite sizes - Adjust --pack-padding

    "},{"location":"tools/sprite_compiler/advanced_features/#performance-issues","title":"Performance Issues","text":"

    Slow compilation: - Use --parallel for batch - Enable --cache - Reduce image sizes - Use --incremental

    "},{"location":"tools/sprite_compiler/advanced_features/#see-also","title":"See Also","text":"
    • Usage Guide - Basic usage examples
    • Overview - Compiler overview
    • Manual - Sprites - Using sprites in games
    "},{"location":"tools/sprite_compiler/installation/","title":"Sprite Compiler Installation","text":"

    This guide walks you through installing the PixelRoot32 Sprite Compiler on your system.

    "},{"location":"tools/sprite_compiler/installation/#prerequisites","title":"Prerequisites","text":""},{"location":"tools/sprite_compiler/installation/#required-software","title":"Required Software","text":"
    • Node.js: Version 14.0 or higher
    • npm: Usually included with Node.js
    • Git: For cloning from source (optional)
    "},{"location":"tools/sprite_compiler/installation/#verify-prerequisites","title":"Verify Prerequisites","text":"

    Check if Node.js is installed:

    node --version\n# Should show v14.0.0 or higher\n

    Check if npm is installed:

    npm --version\n# Should show version number\n

    If not installed, download from nodejs.org

    "},{"location":"tools/sprite_compiler/installation/#installation-methods","title":"Installation Methods","text":""},{"location":"tools/sprite_compiler/installation/#method-1-npm-package-recommended","title":"Method 1: NPM Package (Recommended)","text":"

    The easiest way to install the Sprite Compiler is via npm:

    npm install -g pr32-sprite-compiler\n

    This installs the compiler globally, making it available from any directory.

    Verify installation:

    pr32-sprite-compiler --version\n

    "},{"location":"tools/sprite_compiler/installation/#method-2-from-source","title":"Method 2: From Source","text":"

    If you want the latest development version or need to customize:

    "},{"location":"tools/sprite_compiler/installation/#step-1-clone-repository","title":"Step 1: Clone Repository","text":"
    git clone https://github.com/Gperez88/pr32-sprite-compiler.git\ncd pr32-sprite-compiler\n
    "},{"location":"tools/sprite_compiler/installation/#step-2-install-dependencies","title":"Step 2: Install Dependencies","text":"
    npm install\n
    "},{"location":"tools/sprite_compiler/installation/#step-3-install-locally-optional","title":"Step 3: Install Locally (Optional)","text":"

    To use the compiler from anywhere:

    npm link\n

    This creates a global symlink to the local installation.

    "},{"location":"tools/sprite_compiler/installation/#step-4-verify-installation","title":"Step 4: Verify Installation","text":"
    pr32-sprite-compiler --version\n
    "},{"location":"tools/sprite_compiler/installation/#method-3-local-project-installation","title":"Method 3: Local Project Installation","text":"

    For project-specific installation (not global):

    cd your-pixelroot32-project\nnpm install pr32-sprite-compiler --save-dev\n

    Then use via npx:

    npx pr32-sprite-compiler input.png output.h\n

    "},{"location":"tools/sprite_compiler/installation/#platform-specific-instructions","title":"Platform-Specific Instructions","text":""},{"location":"tools/sprite_compiler/installation/#windows","title":"Windows","text":""},{"location":"tools/sprite_compiler/installation/#using-npm-recommended","title":"Using npm (Recommended)","text":"
    1. Install Node.js from nodejs.org
    2. Download the Windows installer
    3. Run the installer
    4. Restart your terminal/command prompt

    5. Open Command Prompt or PowerShell

    6. Install globally:

      npm install -g pr32-sprite-compiler\n

    7. Verify:

      pr32-sprite-compiler --version\n

    "},{"location":"tools/sprite_compiler/installation/#troubleshooting-windows-issues","title":"Troubleshooting Windows Issues","text":"

    \"pr32-sprite-compiler is not recognized\": - Ensure Node.js is in your PATH - Restart terminal after installation - Try using full path: C:\\Users\\YourName\\AppData\\Roaming\\npm\\pr32-sprite-compiler.cmd

    Permission errors: - Run terminal as Administrator - Or install locally: npm install pr32-sprite-compiler (without -g)

    "},{"location":"tools/sprite_compiler/installation/#linux","title":"Linux","text":""},{"location":"tools/sprite_compiler/installation/#using-npm","title":"Using npm","text":"
    1. Install Node.js (if not already installed):

    Ubuntu/Debian:

    curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -\nsudo apt-get install -y nodejs\n

    Fedora/RHEL:

    sudo dnf install nodejs npm\n

    1. Install Sprite Compiler:

      sudo npm install -g pr32-sprite-compiler\n

    2. Verify:

      pr32-sprite-compiler --version\n

    "},{"location":"tools/sprite_compiler/installation/#troubleshooting-linux-issues","title":"Troubleshooting Linux Issues","text":"

    Permission denied: - Use sudo for global installation - Or install locally without -g flag

    Command not found: - Check npm global bin path: npm config get prefix - Add to PATH if needed: export PATH=$PATH:$(npm config get prefix)/bin

    "},{"location":"tools/sprite_compiler/installation/#macos","title":"macOS","text":""},{"location":"tools/sprite_compiler/installation/#using-npm_1","title":"Using npm","text":"
    1. Install Node.js:
    2. Download from nodejs.org
    3. Or use Homebrew: brew install node

    4. Install Sprite Compiler:

      npm install -g pr32-sprite-compiler\n

    5. Verify:

      pr32-sprite-compiler --version\n

    "},{"location":"tools/sprite_compiler/installation/#using-homebrew-alternative","title":"Using Homebrew (Alternative)","text":"

    If available as a Homebrew formula:

    brew install pr32-sprite-compiler\n

    "},{"location":"tools/sprite_compiler/installation/#gui-version-installation","title":"GUI Version Installation","text":"

    If a GUI version is available:

    "},{"location":"tools/sprite_compiler/installation/#windows_1","title":"Windows","text":"

    Download the installer from the releases page and run it.

    "},{"location":"tools/sprite_compiler/installation/#linux_1","title":"Linux","text":"
    # Download AppImage or .deb package\n# Make executable and run\nchmod +x pr32-sprite-compiler-gui.AppImage\n./pr32-sprite-compiler-gui.AppImage\n
    "},{"location":"tools/sprite_compiler/installation/#macos_1","title":"macOS","text":"

    Download the .dmg file from releases and install.

    "},{"location":"tools/sprite_compiler/installation/#verification","title":"Verification","text":"

    After installation, verify everything works:

    "},{"location":"tools/sprite_compiler/installation/#test-basic-functionality","title":"Test Basic Functionality","text":"
    1. Create a test image:
    2. Create an 8x8 pixel PNG image (black and white)
    3. Save as test.png

    4. Run compiler:

      pr32-sprite-compiler test.png test_output.h\n

    5. Check output:

    6. File test_output.h should be created
    7. Should contain sprite data arrays
    "},{"location":"tools/sprite_compiler/installation/#check-version","title":"Check Version","text":"
    pr32-sprite-compiler --version\n
    "},{"location":"tools/sprite_compiler/installation/#check-help","title":"Check Help","text":"
    pr32-sprite-compiler --help\n
    "},{"location":"tools/sprite_compiler/installation/#updating","title":"Updating","text":""},{"location":"tools/sprite_compiler/installation/#update-via-npm","title":"Update via npm","text":"
    npm update -g pr32-sprite-compiler\n
    "},{"location":"tools/sprite_compiler/installation/#update-from-source","title":"Update from Source","text":"
    cd pr32-sprite-compiler\ngit pull\nnpm install\n
    "},{"location":"tools/sprite_compiler/installation/#uninstallation","title":"Uninstallation","text":""},{"location":"tools/sprite_compiler/installation/#remove-global-installation","title":"Remove Global Installation","text":"
    npm uninstall -g pr32-sprite-compiler\n
    "},{"location":"tools/sprite_compiler/installation/#remove-local-installation","title":"Remove Local Installation","text":"
    npm uninstall pr32-sprite-compiler\n
    "},{"location":"tools/sprite_compiler/installation/#troubleshooting","title":"Troubleshooting","text":""},{"location":"tools/sprite_compiler/installation/#common-issues","title":"Common Issues","text":"

    \"Command not found\" after installation: - Restart your terminal - Check npm global bin path: npm config get prefix - Verify PATH includes npm bin directory

    Permission errors: - On Linux/macOS: Use sudo for global install - Or install locally without -g flag - On Windows: Run terminal as Administrator

    Module not found errors: - Reinstall: npm install -g pr32-sprite-compiler - Clear npm cache: npm cache clean --force

    Version conflicts: - Check Node.js version: node --version - Update Node.js if version is too old - Use nvm (Node Version Manager) to manage versions

    "},{"location":"tools/sprite_compiler/installation/#getting-help","title":"Getting Help","text":"
    • Check the Usage Guide for usage examples
    • Review Troubleshooting for common issues
    • Open an issue on GitHub if problems persist
    "},{"location":"tools/sprite_compiler/installation/#next-steps","title":"Next Steps","text":"

    Once installed, proceed to: - Usage Guide - Learn how to use the compiler - Advanced Features - Explore advanced options

    "},{"location":"tools/sprite_compiler/installation/#see-also","title":"See Also","text":"
    • Overview - What the Sprite Compiler does
    • Available Tools - All PixelRoot32 tools
    "},{"location":"tools/sprite_compiler/overview/","title":"Sprite Compiler Overview","text":"

    The Sprite Compiler (pr32-sprite-compiler) is a command-line tool that converts PNG images into PixelRoot32 sprite data formats. It automates the process of creating sprite arrays from image files, saving you time and ensuring correct format conversion.

    "},{"location":"tools/sprite_compiler/overview/#what-it-does","title":"What It Does","text":"

    The Sprite Compiler takes bitmap images (PNG) and converts them into C/C++ header files containing:

    • Sprite data arrays: Optimized uint16_t arrays for 1bpp sprites
    • Sprite structures: Ready-to-use pixelroot32::graphics::Sprite definitions
    • Multi-sprite support: Handles sprite sheets and animations
    • Format options: Supports 1bpp (standard), 2bpp, and 4bpp formats
    "},{"location":"tools/sprite_compiler/overview/#key-features","title":"Key Features","text":""},{"location":"tools/sprite_compiler/overview/#format-support","title":"\u2705 Format Support","text":"
    • 1bpp (Monochrome): Standard format, most memory-efficient
    • 2bpp (4 colors): Experimental, requires build flags
    • 4bpp (16 colors): Experimental, requires build flags
    • MultiSprite: Layer compositions for complex sprites
    "},{"location":"tools/sprite_compiler/overview/#batch-processing","title":"\u2705 Batch Processing","text":"

    Process multiple images at once:

    pr32-sprite-compiler --batch sprites/*.png --output-dir generated/\n

    "},{"location":"tools/sprite_compiler/overview/#sprite-sheets","title":"\u2705 Sprite Sheets","text":"

    Automatically split sprite sheets into individual sprites:

    pr32-sprite-compiler sheet.png output.h --sheet 8x8 --count 16\n

    "},{"location":"tools/sprite_compiler/overview/#custom-palettes","title":"\u2705 Custom Palettes","text":"

    Use custom color palettes for conversion:

    pr32-sprite-compiler input.png output.h --palette custom.json\n

    "},{"location":"tools/sprite_compiler/overview/#gui-version-optional","title":"\u2705 GUI Version (Optional)","text":"

    Visual interface for: - Drag and drop image processing - Real-time preview of sprite data - Visual settings adjustment - Export to header files

    "},{"location":"tools/sprite_compiler/overview/#input-requirements","title":"Input Requirements","text":""},{"location":"tools/sprite_compiler/overview/#supported-formats","title":"Supported Formats","text":"
    • PNG: Primary format (recommended)
    • Indexed color PNG: Best for 1bpp conversion
    • Grayscale PNG: Automatically converted to 1bpp
    • RGB PNG: Converted using threshold or palette
    "},{"location":"tools/sprite_compiler/overview/#image-constraints","title":"Image Constraints","text":"

    For 1bpp sprites: - Maximum width: 16 pixels - Height: Any (typically 8, 16, 32 pixels) - Colors: Black and white (or converted automatically)

    For 2bpp sprites: - Maximum width: 16 pixels - Colors: Up to 4 colors

    For 4bpp sprites: - Maximum width: 16 pixels - Colors: Up to 16 colors

    "},{"location":"tools/sprite_compiler/overview/#output-format","title":"Output Format","text":"

    The compiler generates C++ header files with:

    #ifndef SPRITE_DATA_H\n#define SPRITE_DATA_H\n\n#include <stdint.h>\n#include <graphics/Sprite.h>\n\n// Sprite data array\nstatic const uint16_t MY_SPRITE_DATA[] = {\n    0b00111100,  // Row 0\n    0b01111110,  // Row 1\n    0b11111111,  // Row 2\n    // ... more rows\n};\n\n// Sprite structure\nstatic const pixelroot32::graphics::Sprite MY_SPRITE = {\n    MY_SPRITE_DATA,  // data pointer\n    8,                // width\n    8                 // height\n};\n\n#endif\n
    "},{"location":"tools/sprite_compiler/overview/#use-cases","title":"Use Cases","text":""},{"location":"tools/sprite_compiler/overview/#1-single-sprite-conversion","title":"1. Single Sprite Conversion","text":"

    Convert a single image to a sprite:

    pr32-sprite-compiler player.png player_sprite.h --name PLAYER_SPRITE\n

    "},{"location":"tools/sprite_compiler/overview/#2-animation-frames","title":"2. Animation Frames","text":"

    Convert multiple frames for animation:

    pr32-sprite-compiler --batch walk_*.png --output-dir animations/ --prefix WALK_\n

    "},{"location":"tools/sprite_compiler/overview/#3-sprite-sheet-processing","title":"3. Sprite Sheet Processing","text":"

    Split a sprite sheet into individual sprites:

    pr32-sprite-compiler characters.png output.h --sheet 16x16 --count 8\n

    "},{"location":"tools/sprite_compiler/overview/#4-batch-asset-processing","title":"4. Batch Asset Processing","text":"

    Process entire asset directories:

    pr32-sprite-compiler --batch assets/sprites/*.png --output-dir src/sprites/\n

    "},{"location":"tools/sprite_compiler/overview/#workflow-integration","title":"Workflow Integration","text":""},{"location":"tools/sprite_compiler/overview/#typical-development-workflow","title":"Typical Development Workflow","text":"
    1. Create sprites in your image editor (Aseprite, Piskel, GIMP, etc.)
    2. Save as PNG with appropriate dimensions
    3. Run compiler to generate header files
    4. Include headers in your PixelRoot32 project
    5. Use sprites in your game code
    "},{"location":"tools/sprite_compiler/overview/#automation-example","title":"Automation Example","text":"
    #!/bin/bash\n# build-sprites.sh\n\n# Compile all sprites\npr32-sprite-compiler assets/sprites/*.png --output-dir src/sprites/\n\n# Continue with your build process\nplatformio run\n
    "},{"location":"tools/sprite_compiler/overview/#advantages-over-manual-creation","title":"Advantages Over Manual Creation","text":""},{"location":"tools/sprite_compiler/overview/#time-saving","title":"\u2705 Time Saving","text":"
    • No manual bit pattern conversion
    • Automatic format optimization
    • Batch processing multiple sprites
    "},{"location":"tools/sprite_compiler/overview/#accuracy","title":"\u2705 Accuracy","text":"
    • Correct bit ordering
    • Proper array formatting
    • Valid C++ syntax
    "},{"location":"tools/sprite_compiler/overview/#consistency","title":"\u2705 Consistency","text":"
    • Uniform naming conventions
    • Standardized output format
    • Consistent code structure
    "},{"location":"tools/sprite_compiler/overview/#maintainability","title":"\u2705 Maintainability","text":"
    • Easy to regenerate from source images
    • Version control friendly
    • Clear separation of assets and code
    "},{"location":"tools/sprite_compiler/overview/#limitations","title":"Limitations","text":"
    • Width limit: 16 pixels for 1bpp (hardware constraint)
    • Color depth: Limited by format (1bpp = 2 colors, 2bpp = 4 colors, etc.)
    • File format: Primarily PNG (other formats may require conversion)
    "},{"location":"tools/sprite_compiler/overview/#next-steps","title":"Next Steps","text":"
    • Installation Guide - Set up the compiler
    • Usage Guide - Learn how to use it
    • Advanced Features - Explore advanced options
    "},{"location":"tools/sprite_compiler/overview/#see-also","title":"See Also","text":"
    • Manual - Sprites and Animation - Using sprites in games
    • Code Examples - Sprites - Sprite usage examples
    • Available Tools - All PixelRoot32 tools
    "},{"location":"tools/sprite_compiler/usage_guide/","title":"Sprite Compiler Usage Guide","text":"

    Complete guide to using the PixelRoot32 Sprite Compiler for converting images to sprite data.

    "},{"location":"tools/sprite_compiler/usage_guide/#basic-usage","title":"Basic Usage","text":""},{"location":"tools/sprite_compiler/usage_guide/#simple-conversion","title":"Simple Conversion","text":"

    Convert a single PNG image to a sprite header file:

    pr32-sprite-compiler input.png output.h\n

    This creates output.h with sprite data from input.png.

    "},{"location":"tools/sprite_compiler/usage_guide/#with-custom-name","title":"With Custom Name","text":"

    Specify a custom name for the sprite:

    pr32-sprite-compiler player.png player_sprite.h --name PLAYER_SPRITE\n

    The generated sprite will be named PLAYER_SPRITE instead of the default.

    "},{"location":"tools/sprite_compiler/usage_guide/#command-line-options","title":"Command Line Options","text":""},{"location":"tools/sprite_compiler/usage_guide/#basic-options","title":"Basic Options","text":"
    pr32-sprite-compiler [input] [output] [options]\n

    Required: - input: Input PNG image file - output: Output header file path

    Common Options: - --name NAME: Custom sprite name (default: derived from filename) - --format FORMAT: Output format (1bpp, 2bpp, 4bpp) - --width W: Force sprite width (pixels) - --height H: Force sprite height (pixels)

    "},{"location":"tools/sprite_compiler/usage_guide/#format-selection","title":"Format Selection","text":"

    1bpp (Monochrome) - Default:

    pr32-sprite-compiler sprite.png output.h --format 1bpp\n

    2bpp (4 colors) - Experimental:

    pr32-sprite-compiler sprite.png output.h --format 2bpp\n

    4bpp (16 colors) - Experimental:

    pr32-sprite-compiler sprite.png output.h --format 4bpp\n

    "},{"location":"tools/sprite_compiler/usage_guide/#step-by-step-examples","title":"Step-by-Step Examples","text":""},{"location":"tools/sprite_compiler/usage_guide/#example-1-simple-player-sprite","title":"Example 1: Simple Player Sprite","text":"

    Step 1: Create Image - Create an 8x8 pixel PNG image - Use black and white colors - Save as player.png

    Step 2: Compile

    pr32-sprite-compiler player.png player_sprite.h --name PLAYER_SPRITE\n

    Step 3: Use in Code

    #include \"player_sprite.h\"\n\nvoid draw() {\n    renderer.drawSprite(PLAYER_SPRITE, 100, 100, Color::White);\n}\n

    "},{"location":"tools/sprite_compiler/usage_guide/#example-2-multiple-animation-frames","title":"Example 2: Multiple Animation Frames","text":"

    Step 1: Prepare Images - Create frames: walk_0.png, walk_1.png, walk_2.png - All same size (e.g., 16x16)

    Step 2: Batch Compile

    pr32-sprite-compiler --batch walk_*.png --output-dir animations/ --prefix WALK_\n

    Step 3: Use in Animation

    #include \"animations/walk_0.h\"\n#include \"animations/walk_1.h\"\n#include \"animations/walk_2.h\"\n\nconst Sprite* WALK_FRAMES[] = {\n    &WALK_0_SPRITE,\n    &WALK_1_SPRITE,\n    &WALK_2_SPRITE\n};\n

    "},{"location":"tools/sprite_compiler/usage_guide/#example-3-sprite-sheet","title":"Example 3: Sprite Sheet","text":"

    Step 1: Create Sprite Sheet - Create a 64x64 image with 4x4 grid of 16x16 sprites - Save as characters.png

    Step 2: Split Sheet

    pr32-sprite-compiler characters.png characters.h --sheet 16x16 --count 16\n

    Step 3: Use Individual Sprites

    #include \"characters.h\"\n\n// Sprites named CHARACTER_0, CHARACTER_1, etc.\nrenderer.drawSprite(CHARACTER_0, 50, 50, Color::White);\nrenderer.drawSprite(CHARACTER_1, 70, 50, Color::White);\n

    "},{"location":"tools/sprite_compiler/usage_guide/#batch-processing","title":"Batch Processing","text":""},{"location":"tools/sprite_compiler/usage_guide/#process-multiple-files","title":"Process Multiple Files","text":"

    Process all PNG files in a directory:

    pr32-sprite-compiler --batch sprites/*.png --output-dir generated/\n
    "},{"location":"tools/sprite_compiler/usage_guide/#with-options","title":"With Options","text":"

    Apply options to all files:

    pr32-sprite-compiler --batch assets/*.png \\\n    --output-dir src/sprites/ \\\n    --format 1bpp \\\n    --prefix SPRITE_\n
    "},{"location":"tools/sprite_compiler/usage_guide/#recursive-processing","title":"Recursive Processing","text":"

    Process subdirectories:

    pr32-sprite-compiler --batch assets/**/*.png --output-dir generated/\n
    "},{"location":"tools/sprite_compiler/usage_guide/#sprite-sheets","title":"Sprite Sheets","text":""},{"location":"tools/sprite_compiler/usage_guide/#automatic-splitting","title":"Automatic Splitting","text":"

    Split a sprite sheet into individual sprites:

    pr32-sprite-compiler sheet.png output.h --sheet 8x8 --count 16\n

    Parameters: - --sheet WxH: Tile size (width x height) - --count N: Number of sprites in sheet

    "},{"location":"tools/sprite_compiler/usage_guide/#grid-layout","title":"Grid Layout","text":"

    Specify grid dimensions:

    pr32-sprite-compiler sheet.png output.h \\\n    --sheet 16x16 \\\n    --grid 4x4 \\\n    --count 16\n

    Parameters: - --grid WxH: Grid dimensions (columns x rows)

    "},{"location":"tools/sprite_compiler/usage_guide/#custom-naming","title":"Custom Naming","text":"

    Name sprites with index:

    pr32-sprite-compiler sheet.png output.h \\\n    --sheet 8x8 \\\n    --count 8 \\\n    --prefix CHARACTER_ \\\n    --indexed\n

    Generates: CHARACTER_0, CHARACTER_1, etc.

    "},{"location":"tools/sprite_compiler/usage_guide/#custom-palettes","title":"Custom Palettes","text":""},{"location":"tools/sprite_compiler/usage_guide/#using-palette-file","title":"Using Palette File","text":"

    Convert with custom color palette:

    pr32-sprite-compiler sprite.png output.h --palette palette.json\n

    Palette JSON format:

    {\n  \"colors\": [\n    {\"r\": 0, \"g\": 0, \"b\": 0, \"name\": \"black\"},\n    {\"r\": 255, \"g\": 255, \"b\": 255, \"name\": \"white\"}\n  ]\n}\n

    "},{"location":"tools/sprite_compiler/usage_guide/#built-in-palettes","title":"Built-in Palettes","text":"

    Use predefined palettes:

    pr32-sprite-compiler sprite.png output.h --palette nes\npr32-sprite-compiler sprite.png output.h --palette gb\npr32-sprite-compiler sprite.png output.h --palette pico8\n
    "},{"location":"tools/sprite_compiler/usage_guide/#advanced-options","title":"Advanced Options","text":""},{"location":"tools/sprite_compiler/usage_guide/#threshold-for-grayscale","title":"Threshold for Grayscale","text":"

    Set threshold for black/white conversion:

    pr32-sprite-compiler sprite.png output.h --threshold 128\n

    Values: 0-255 (default: 127)

    "},{"location":"tools/sprite_compiler/usage_guide/#dithering","title":"Dithering","text":"

    Enable dithering for better gradients:

    pr32-sprite-compiler sprite.png output.h --dither\n
    "},{"location":"tools/sprite_compiler/usage_guide/#alignment","title":"Alignment","text":"

    Control output alignment:

    pr32-sprite-compiler sprite.png output.h --align 4\n
    "},{"location":"tools/sprite_compiler/usage_guide/#endianness","title":"Endianness","text":"

    Specify byte order:

    pr32-sprite-compiler sprite.png output.h --endian little\npr32-sprite-compiler sprite.png output.h --endian big\n
    "},{"location":"tools/sprite_compiler/usage_guide/#output-customization","title":"Output Customization","text":""},{"location":"tools/sprite_compiler/usage_guide/#namespace","title":"Namespace","text":"

    Wrap output in namespace:

    pr32-sprite-compiler sprite.png output.h --namespace MyGame\n
    "},{"location":"tools/sprite_compiler/usage_guide/#header-guard","title":"Header Guard","text":"

    Custom header guard:

    pr32-sprite-compiler sprite.png output.h --guard MY_SPRITE_H\n
    "},{"location":"tools/sprite_compiler/usage_guide/#include-paths","title":"Include Paths","text":"

    Custom include paths:

    pr32-sprite-compiler sprite.png output.h \\\n    --include \"<graphics/Sprite.h>\" \\\n    --include \"<stdint.h>\"\n
    "},{"location":"tools/sprite_compiler/usage_guide/#integration-with-build-systems","title":"Integration with Build Systems","text":""},{"location":"tools/sprite_compiler/usage_guide/#platformio","title":"PlatformIO","text":"

    Add to platformio.ini:

    [env:esp32dev]\nextra_scripts = \n    pre:scripts/compile_sprites.py\n

    compile_sprites.py:

    Import(\"env\")\nimport subprocess\n\nsubprocess.run([\n    \"pr32-sprite-compiler\",\n    \"--batch\", \"assets/sprites/*.png\",\n    \"--output-dir\", \"src/sprites/\"\n])\n

    "},{"location":"tools/sprite_compiler/usage_guide/#makefile","title":"Makefile","text":"
    SPRITES = $(wildcard assets/sprites/*.png)\nSPRITE_HEADERS = $(SPRITES:assets/sprites/%.png=src/sprites/%.h)\n\nsrc/sprites/%.h: assets/sprites/%.png\n    pr32-sprite-compiler $< $@ --name $(shell basename $< .png | tr '[:lower:]' '[:upper:]')_SPRITE\n\nsprites: $(SPRITE_HEADERS)\n
    "},{"location":"tools/sprite_compiler/usage_guide/#cmake","title":"CMake","text":"
    file(GLOB SPRITE_FILES \"assets/sprites/*.png\")\n\nforeach(SPRITE ${SPRITE_FILES})\n    get_filename_component(SPRITE_NAME ${SPRITE} NAME_WE)\n    add_custom_command(\n        OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/sprites/${SPRITE_NAME}.h\n        COMMAND pr32-sprite-compiler\n        ARGS ${SPRITE} ${CMAKE_CURRENT_SOURCE_DIR}/src/sprites/${SPRITE_NAME}.h\n        DEPENDS ${SPRITE}\n    )\nendforeach()\n
    "},{"location":"tools/sprite_compiler/usage_guide/#gui-usage-if-available","title":"GUI Usage (If Available)","text":""},{"location":"tools/sprite_compiler/usage_guide/#opening-gui","title":"Opening GUI","text":"
    pr32-sprite-compiler --gui\n

    Or launch the GUI application directly.

    "},{"location":"tools/sprite_compiler/usage_guide/#gui-workflow","title":"GUI Workflow","text":"
    1. Drag and drop images into the window
    2. Preview sprite data in real-time
    3. Adjust settings visually (format, threshold, etc.)
    4. Export to header files
    5. Batch process multiple files
    "},{"location":"tools/sprite_compiler/usage_guide/#gui-features","title":"GUI Features","text":"
    • Visual preview of sprite conversion
    • Real-time threshold adjustment
    • Palette selection
    • Batch processing interface
    • Export options
    "},{"location":"tools/sprite_compiler/usage_guide/#best-practices","title":"Best Practices","text":""},{"location":"tools/sprite_compiler/usage_guide/#image-preparation","title":"Image Preparation","text":"
    • Use indexed color PNG for best results
    • Keep sprites small (8x8, 16x16, 32x32)
    • Use black and white for 1bpp
    • Limit colors for 2bpp/4bpp formats
    "},{"location":"tools/sprite_compiler/usage_guide/#file-organization","title":"File Organization","text":"
    project/\n\u251c\u2500\u2500 assets/\n\u2502   \u2514\u2500\u2500 sprites/\n\u2502       \u251c\u2500\u2500 player.png\n\u2502       \u251c\u2500\u2500 enemy.png\n\u2502       \u2514\u2500\u2500 items.png\n\u251c\u2500\u2500 src/\n\u2502   \u2514\u2500\u2500 sprites/          # Generated headers\n\u2502       \u251c\u2500\u2500 player.h\n\u2502       \u251c\u2500\u2500 enemy.h\n\u2502       \u2514\u2500\u2500 items.h\n\u2514\u2500\u2500 platformio.ini\n
    "},{"location":"tools/sprite_compiler/usage_guide/#naming-conventions","title":"Naming Conventions","text":"
    • Use descriptive names: player_walk_0.png \u2192 PLAYER_WALK_0_SPRITE
    • Be consistent: All caps for sprite names
    • Use prefixes: ENEMY_, PLAYER_, ITEM_
    "},{"location":"tools/sprite_compiler/usage_guide/#version-control","title":"Version Control","text":"
    • Commit generated headers (they're part of the build)
    • Or add to .gitignore and regenerate on build
    • Keep source images in version control
    "},{"location":"tools/sprite_compiler/usage_guide/#troubleshooting","title":"Troubleshooting","text":""},{"location":"tools/sprite_compiler/usage_guide/#common-issues","title":"Common Issues","text":"

    \"Image too large\": - Sprites must be \u2264 16 pixels wide for 1bpp - Resize image or split into multiple sprites

    \"Colors not converting correctly\": - Use indexed color PNG - For 1bpp: Use only black and white - For 2bpp: Use exactly 4 colors - For 4bpp: Use up to 16 colors

    \"Output file not found\": - Check write permissions - Verify output directory exists - Use absolute paths if needed

    \"Invalid format\": - Ensure input is PNG format - Check file is not corrupted - Try re-saving image in image editor

    "},{"location":"tools/sprite_compiler/usage_guide/#getting-help","title":"Getting Help","text":"
    pr32-sprite-compiler --help\n

    Shows all available options and usage.

    "},{"location":"tools/sprite_compiler/usage_guide/#next-steps","title":"Next Steps","text":"
    • Advanced Features - Explore advanced options
    • Overview - Learn more about the compiler
    • Manual - Sprites - Using sprites in games
    "},{"location":"tools/sprite_compiler/usage_guide/#see-also","title":"See Also","text":"
    • Code Examples - Sprites - Sprite usage examples
    • Troubleshooting - Common issues and solutions
    "},{"location":"tools/tilemap_editor/installation/","title":"Installation Guide","text":"

    The PixelRoot32 Tilemap Editor can be run directly from source or as a standalone executable on Windows.

    "},{"location":"tools/tilemap_editor/installation/#1-requirements","title":"1. Requirements","text":"
    • Python 3.13+ (if running from source).
    • Windows 10/11 (recommended).
    "},{"location":"tools/tilemap_editor/installation/#2-install-from-source","title":"2. Install from Source","text":""},{"location":"tools/tilemap_editor/installation/#21-clone-the-repository","title":"2.1 Clone the Repository","text":"
    git clone https://github.com/Gperez88/PixelRoot32-Tilemap-Editor.git\ncd PixelRoot32-Tilemap-Editor\n
    "},{"location":"tools/tilemap_editor/installation/#22-install-dependencies","title":"2.2 Install Dependencies","text":"

    The editor uses several Python libraries for the GUI and image processing:

    pip install ttkbootstrap pillow jinja2\n
    "},{"location":"tools/tilemap_editor/installation/#23-run-the-editor","title":"2.3 Run the Editor","text":"
    python main.py\n
    "},{"location":"tools/tilemap_editor/installation/#3-standalone-executable-windows","title":"3. Standalone Executable (Windows)","text":"

    For a more convenient experience, you can use the pre-compiled version:

    1. Go to the Releases section of the repository.
    2. Download the latest PixelRoot32-Editor-win64.zip.
    3. Extract the contents to a folder.
    4. Run PixelRoot32-Editor.exe.

    Note: No Python installation is required to run the standalone executable.

    "},{"location":"tools/tilemap_editor/installation/#4-building-your-own-executable","title":"4. Building your own Executable","text":"

    If you want to package the editor yourself:

    1. Install PyInstaller:
      pip install pyinstaller\n
    2. Run the build command using the provided .spec file:
      pyinstaller pixelroot32_editor.spec\n
    3. The executable will be available in the dist/ folder.
    "},{"location":"tools/tilemap_editor/overview/","title":"Tilemap Editor Overview","text":"

    The PixelRoot32 Tilemap Editor is a powerful visual tool designed to create complex multi-layered tile-based maps for the PixelRoot32 engine. It simplifies the process of designing game environments, managing tilesets, and exporting optimized C++ code.

    "},{"location":"tools/tilemap_editor/overview/#what-it-does","title":"What It Does","text":"

    The Tilemap Editor allows you to:

    • Visual Design: Paint tiles directly onto a canvas with layers and transparency.
    • Tileset Management: Import PNG images as tilesets and select single or multiple tiles.
    • Multi-Layer Support: Organize your map into up to 8 layers for parallax effects or depth.
    • Optimized Export: Generate C++ header and source files compatible with the PixelRoot32 renderer.
    • BPP Support: Export maps in 1bpp, 2bpp, or 4bpp formats to balance memory usage and color depth.
    "},{"location":"tools/tilemap_editor/overview/#key-features","title":"Key Features","text":""},{"location":"tools/tilemap_editor/overview/#visual-editing-tools","title":"\u2705 Visual Editing Tools","text":"
    • Brush: Paint individual tiles or patterns.
    • Eraser: Remove tiles from the active layer.
    • Rectangle Fill: Quickly fill areas with a specific tile.
    • Pipette: Pick an existing tile from the canvas.
    "},{"location":"tools/tilemap_editor/overview/#multi-layer-system","title":"\u2705 Multi-Layer System","text":"
    • Visibility Toggle: Hide/show layers to focus on specific parts of the map.
    • Opacity Control: Adjust layer transparency for complex blending effects.
    • Layer Reordering: Change the render order of your tilemaps.
    "},{"location":"tools/tilemap_editor/overview/#tileset-selector","title":"\u2705 Tileset Selector","text":"
    • Smart Selection: Drag and select a rectangular area of tiles.
    • Multiple Tilesets: Support for multiple tilesets per project (planned).
    • Auto-import: Automatically detects tile size from the imported image.
    "},{"location":"tools/tilemap_editor/overview/#engine-integration","title":"\u2705 Engine Integration","text":"
    • Workspace Selection: Link the editor to your PixelRoot32 projects directory.
    • Direct Export: Files are generated with the correct namespaces and structures for immediate use.
    • BPP Compatibility: Ensures exported data matches the engine's expected format for 1bpp, 2bpp, and 4bpp.
    "},{"location":"tools/tilemap_editor/overview/#data-formats","title":"Data Formats","text":""},{"location":"tools/tilemap_editor/overview/#project-file-pr32scene","title":"Project File (.pr32scene)","text":"

    The editor uses a custom JSON-based format to save your project state, including: - Tileset metadata (path, tile size, spacing). - Layer data (tile indices, width, height, position). - Project settings (BPP, namespace).

    "},{"location":"tools/tilemap_editor/overview/#exported-c","title":"Exported C++","text":"

    The editor generates .h and .cpp files containing: - Tilemap Data: Packed arrays of tile indices. - Tilemap Structures: pixelroot32::graphics::TileMap (or TileMap2bpp/TileMap4bpp) definitions. - Scene Headers: Convenient entry points for loading entire maps into your game.

    "},{"location":"tools/tilemap_editor/usage_guide/","title":"Usage Guide","text":"

    This guide covers the basic workflow for creating and exporting a tilemap using the PixelRoot32 Tilemap Editor.

    "},{"location":"tools/tilemap_editor/usage_guide/#1-creating-a-new-project","title":"1. Creating a New Project","text":"
    1. Launch the editor.
    2. Go to File > New Project.
    3. Enter the project name and select the base Color Depth (BPP):
    4. 1bpp: Monochrome (2 colors).
    5. 2bpp: 4 colors.
    6. 4bpp: 16 colors.
    7. Set the Tile Size (e.g., 8x8, 16x16).
    "},{"location":"tools/tilemap_editor/usage_guide/#2-importing-a-tileset","title":"2. Importing a Tileset","text":"
    1. In the Tileset panel, click on Load Tileset.
    2. Select a PNG image containing your tiles.
    3. The image will be sliced into tiles based on the tile size set in the project.
    "},{"location":"tools/tilemap_editor/usage_guide/#3-painting-tiles","title":"3. Painting Tiles","text":"
    1. Select a tile (or a range of tiles) from the Tileset panel.
    2. Select the Brush tool (Shortcut: B).
    3. Click and drag on the canvas to paint.
    4. Use the Layers panel to switch between different layers.
    "},{"location":"tools/tilemap_editor/usage_guide/#4-selection-and-transformations","title":"4. Selection and Transformations","text":"
    • Single Selection: Click on a tile in the tileset.
    • Area Selection: Click and drag in the tileset to select a rectangular block of tiles.
    • Pipette: Press P and click on the canvas to pick the tile under the cursor.
    "},{"location":"tools/tilemap_editor/usage_guide/#5-exporting-to-c","title":"5. Exporting to C++","text":"
    1. Ensure your Workspace Path is correctly set in Project Settings. This should point to your PixelRoot32 project's src or include directory.
    2. Click on File > Export.
    3. The editor will generate:
    4. <ProjectName>_data.h and .cpp: Containing the raw tile data and structures.
    5. <ProjectName>_scene.h: A high-level header to include in your game.
    "},{"location":"tools/tilemap_editor/usage_guide/#6-keyboard-shortcuts","title":"6. Keyboard Shortcuts","text":"Shortcut Action B Brush Tool E Eraser Tool R Rectangle Fill Tool P Pipette Tool Space + Drag Pan Canvas Mouse Wheel Zoom In/Out Ctrl + N New Project Ctrl + S Save Project Ctrl + E Export Project Esc Close floating panels"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"PixelRoot32 Documentation","text":"

    PixelRoot32 is a lightweight 2D game engine designed for ESP32 and native desktop targets. This site provides official, versioned documentation with clear guides, conceptual explanations, API references, and complete examples to help you build games efficiently.

    "},{"location":"#quick-links","title":"Quick Links","text":"
    • What is PixelRoot32? - Start here to understand the engine
    • Your First Project - Get up and running quickly
    • Fundamental Concepts - Learn the core concepts
    • Manual - Complete user guide
    • API Reference - Complete API documentation
    • Examples - Complete game examples
    • Tools - Available tools
    • FAQ - FAQ and troubleshooting
    "},{"location":"#getting-started","title":"Getting Started","text":"

    New to PixelRoot32? Follow this learning path:

    1. What is PixelRoot32? - Understand what the engine is and what it can do
    2. Why PixelRoot32? - Learn the advantages and use cases
    3. Fundamental Concepts - Learn the core architecture concepts
    4. Your First Project - Create and run your first project
    "},{"location":"#about-this-documentation","title":"About This Documentation","text":"
    • Professional technical English across all pages
    • Search-enabled, mobile-friendly UI
    • Versioned with mike (stable/dev/experimental)
    • Cross-linked concepts, API, and examples
    • Progressive learning path from basics to advanced topics
    "},{"location":"api_reference/audio/audio_config/","title":"AudioConfig","text":"

    Configuration for the Audio subsystem.

    "},{"location":"api_reference/audio/audio_config/#description","title":"Description","text":"

    AudioConfig is a simple struct that holds configuration settings for the audio system, including the audio backend and sample rate. It is passed to AudioEngine during construction.

    "},{"location":"api_reference/audio/audio_config/#namespace","title":"Namespace","text":"
    namespace pixelroot32::audio {\n    struct AudioConfig {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/audio/audio_config/#structure","title":"Structure","text":""},{"location":"api_reference/audio/audio_config/#audiobackend-backend","title":"AudioBackend* backend","text":"

    Pointer to the platform-specific audio backend implementation.

    Type: AudioBackend*

    Access: Read-write

    Default: nullptr

    Notes: - Must be set to a valid backend instance - Backend is platform-specific: - ESP32: ESP32_DAC_AudioBackend or ESP32_I2S_AudioBackend - Native: SDL2_AudioBackend - Backend manages the actual audio hardware/API

    Example:

    #ifdef PLATFORM_ESP32\n    pixelroot32::drivers::esp32::ESP32_DAC_AudioBackend dacBackend;\n    audioConfig.backend = &dacBackend;\n#elif PLATFORM_NATIVE\n    pixelroot32::drivers::native::SDL2_AudioBackend sdlBackend;\n    audioConfig.backend = &sdlBackend;\n#endif\n

    "},{"location":"api_reference/audio/audio_config/#int-samplerate","title":"int sampleRate","text":"

    Desired sample rate in Hz.

    Type: int

    Access: Read-write

    Default: 22050

    Notes: - Common values: 11025, 22050, 44100 - Lower rates use less CPU and memory (better for ESP32) - Higher rates provide better quality - Must match backend capabilities

    Example:

    audioConfig.sampleRate = 11025;  // Lower quality, less CPU (ESP32)\naudioConfig.sampleRate = 22050;  // Balanced (default)\naudioConfig.sampleRate = 44100; // Higher quality (Native)\n

    "},{"location":"api_reference/audio/audio_config/#constructors","title":"Constructors","text":""},{"location":"api_reference/audio/audio_config/#audioconfigaudiobackend-backend-nullptr-int-samplerate-22050","title":"AudioConfig(AudioBackend* backend = nullptr, int sampleRate = 22050)","text":"

    Default constructor.

    Parameters: - backend (AudioBackend*, optional): Pointer to the audio backend implementation. Default: nullptr - sampleRate (int, optional): Desired sample rate in Hz. Default: 22050

    Example:

    // Default construction\npixelroot32::audio::AudioConfig audioConfig;\n\n// With backend\npixelroot32::drivers::esp32::ESP32_DAC_AudioBackend dacBackend;\npixelroot32::audio::AudioConfig audioConfig(&dacBackend, 11025);\n

    "},{"location":"api_reference/audio/audio_config/#usage-example","title":"Usage Example","text":""},{"location":"api_reference/audio/audio_config/#esp32-with-dac-backend","title":"ESP32 with DAC Backend","text":"
    #ifdef PLATFORM_ESP32\n#include \"drivers/esp32/ESP32_DAC_AudioBackend.h\"\n\npixelroot32::drivers::esp32::ESP32_DAC_AudioBackend dacBackend;\n\npixelroot32::audio::AudioConfig audioConfig;\naudioConfig.backend = &dacBackend;\naudioConfig.sampleRate = 11025;  // Lower rate for ESP32\n\npixelroot32::audio::AudioEngine audioEngine(audioConfig);\naudioEngine.init();\n#endif\n
    "},{"location":"api_reference/audio/audio_config/#esp32-with-i2s-backend","title":"ESP32 with I2S Backend","text":"
    #ifdef PLATFORM_ESP32\n#include \"drivers/esp32/ESP32_I2S_AudioBackend.h\"\n\npixelroot32::drivers::esp32::ESP32_I2S_AudioBackend i2sBackend;\n\npixelroot32::audio::AudioConfig audioConfig;\naudioConfig.backend = &i2sBackend;\naudioConfig.sampleRate = 22050;  // Higher quality with I2S\n\npixelroot32::audio::AudioEngine audioEngine(audioConfig);\naudioEngine.init();\n#endif\n
    "},{"location":"api_reference/audio/audio_config/#native-with-sdl2-backend","title":"Native with SDL2 Backend","text":"
    #ifdef PLATFORM_NATIVE\n#include \"drivers/native/SDL2_AudioBackend.h\"\n\npixelroot32::drivers::native::SDL2_AudioBackend sdlBackend;\n\npixelroot32::audio::AudioConfig audioConfig;\naudioConfig.backend = &sdlBackend;\naudioConfig.sampleRate = 44100;  // High quality for PC\n\npixelroot32::audio::AudioEngine audioEngine(audioConfig);\naudioEngine.init();\n#endif\n
    "},{"location":"api_reference/audio/audio_config/#complete-engine-setup","title":"Complete Engine Setup","text":"
    #include \"core/Engine.h\"\n#include \"graphics/DisplayConfig.h\"\n#include \"input/InputConfig.h\"\n#include \"audio/AudioConfig.h\"\n\nvoid setup() {\n    // Display config\n    pixelroot32::graphics::DisplayConfig displayConfig;\n    displayConfig.width = 128;\n    displayConfig.height = 128;\n\n    // Input config\n    pixelroot32::input::InputConfig inputConfig;\n    // ... configure input\n\n    // Audio config\n    #ifdef PLATFORM_ESP32\n        pixelroot32::drivers::esp32::ESP32_DAC_AudioBackend dacBackend;\n        pixelroot32::audio::AudioConfig audioConfig(&dacBackend, 11025);\n    #elif PLATFORM_NATIVE\n        pixelroot32::drivers::native::SDL2_AudioBackend sdlBackend;\n        pixelroot32::audio::AudioConfig audioConfig(&sdlBackend, 44100);\n    #endif\n\n    // Create engine with all configs\n    pixelroot32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n    engine.init();\n    engine.run();\n}\n
    "},{"location":"api_reference/audio/audio_config/#platform-specific-considerations","title":"Platform-Specific Considerations","text":""},{"location":"api_reference/audio/audio_config/#esp32-dac-backend","title":"ESP32 DAC Backend","text":"
    • Sample rate: 11025 Hz recommended (lower CPU usage)
    • Quality: Lower quality, but simple setup
    • Pin: Uses GPIO 25 or 26
    • Hardware: Requires simple amplifier circuit
    "},{"location":"api_reference/audio/audio_config/#esp32-i2s-backend","title":"ESP32 I2S Backend","text":"
    • Sample rate: 22050 Hz recommended
    • Quality: Higher quality than DAC
    • Pins: Requires I2S pins (BCLK, LRCK, DOUT)
    • Hardware: Requires external I2S DAC
    "},{"location":"api_reference/audio/audio_config/#native-sdl2-backend","title":"Native SDL2 Backend","text":"
    • Sample rate: 44100 Hz typical
    • Quality: High quality
    • Setup: Requires SDL2 library installed
    • Platforms: Windows, Linux, macOS
    "},{"location":"api_reference/audio/audio_config/#performance-considerations","title":"Performance Considerations","text":"
    • Sample rate: Lower rates use less CPU and memory
    • Backend choice: DAC is simpler but lower quality than I2S
    • Buffer size: Configured in backend, affects latency vs stability
    "},{"location":"api_reference/audio/audio_config/#see-also","title":"See Also","text":"
    • AudioEngine - Audio playback engine
    • Manual - Audio
    • Manual - Platforms and Drivers
    • API Overview
    "},{"location":"api_reference/audio/audio_engine/","title":"AudioEngine","text":"

    Core class for the NES-like audio subsystem.

    "},{"location":"api_reference/audio/audio_engine/#description","title":"Description","text":"

    AudioEngine manages the audio channels (Pulse, Triangle, Noise), mixes their output, and provides the audio stream to the backend. It implements a NES-like audio system with 4 fixed channels: 2 Pulse channels, 1 Triangle channel, and 1 Noise channel.

    The engine is event-driven: you trigger sound effects via playEvent(), and the engine automatically manages channel allocation and playback.

    "},{"location":"api_reference/audio/audio_engine/#namespace","title":"Namespace","text":"
    namespace pixelroot32::audio {\n    class AudioEngine {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/audio/audio_engine/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Engine (manages audio engine instance)
    "},{"location":"api_reference/audio/audio_engine/#constructors","title":"Constructors","text":""},{"location":"api_reference/audio/audio_engine/#audioengineconst-audioconfig-config","title":"AudioEngine(const AudioConfig& config)","text":"

    Constructs the AudioEngine with the given configuration.

    Parameters: - config (const AudioConfig&): Configuration struct containing the backend and parameters (sample rate, etc.)

    Example:

    #include \"audio/AudioEngine.h\"\n#include \"audio/AudioConfig.h\"\n\npixelroot32::audio::AudioConfig audioConfig;\naudioConfig.backend = &audioBackend;  // Platform-specific backend\naudioConfig.sampleRate = 22050;       // 22.05 kHz for retro feel\n\npixelroot32::audio::AudioEngine audioEngine(audioConfig);\naudioEngine.init();\n

    "},{"location":"api_reference/audio/audio_engine/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/audio/audio_engine/#void-init","title":"void init()","text":"

    Initializes the audio subsystem and the backend.

    Returns: - void

    Notes: - Must be called after construction and before use - Initializes the platform-specific audio backend - Safe to call multiple times (idempotent) - Typically called automatically by Engine::init()

    Example:

    AudioEngine audioEngine(audioConfig);\naudioEngine.init();  // Initialize before use\n

    "},{"location":"api_reference/audio/audio_engine/#void-updateunsigned-long-deltatime","title":"void update(unsigned long deltaTime)","text":"

    Updates the audio state based on game time.

    Parameters: - deltaTime (unsigned long): Time elapsed since last frame in milliseconds

    Returns: - void

    Notes: - Should be called from the main game loop (typically via Engine::update()) - Updates channel lifetimes and durations - Automatically stops channels when their duration expires - Must be called every frame for proper audio timing

    Example:

    void update(unsigned long deltaTime) override {\n    // Update audio (called automatically by Engine)\n    engine.getAudioEngine().update(deltaTime);\n\n    // Your game logic...\n}\n

    "},{"location":"api_reference/audio/audio_engine/#void-generatesamplesint16_t-stream-int-length","title":"void generateSamples(int16_t* stream, int length)","text":"

    Fills the provided buffer with mixed audio samples.

    Parameters: - stream (int16_t*): Pointer to the buffer to fill - length (int): Number of samples to generate

    Returns: - void

    Notes: - This method is typically called by the AudioBackend from an audio callback or task - Not usually called directly by game code - Generates 16-bit signed integer PCM samples - Mixes all active channels into a mono stream

    Advanced Usage:

    // Typically not called directly, but if implementing custom backend:\nint16_t buffer[512];\naudioEngine.generateSamples(buffer, 512);\n

    "},{"location":"api_reference/audio/audio_engine/#void-playeventconst-audioevent-event","title":"void playEvent(const AudioEvent& event)","text":"

    Triggers a one-shot sound effect.

    Parameters: - event (const AudioEvent&): The audio event to play

    Returns: - void

    Notes: - Automatically finds an available channel of the correct type - If no channel is available, the event may be dropped (no error) - Events are fire-and-forget (no need to track playback) - Use for sound effects, not background music

    Example:

    // Play a jump sound\npixelroot32::audio::AudioEvent jumpSound{};\njumpSound.type = pixelroot32::audio::WaveType::PULSE;\njumpSound.frequency = 800.0f;\njumpSound.duration = 0.1f;\njumpSound.volume = 0.7f;\njumpSound.duty = 0.5f;\n\nauto& audio = engine.getAudioEngine();\naudio.playEvent(jumpSound);\n\n// Play an explosion sound\npixelroot32::audio::AudioEvent explosion{};\nexplosion.type = pixelroot32::audio::WaveType::NOISE;\nexplosion.frequency = 1000.0f;\nexplosion.duration = 0.3f;\nexplosion.volume = 0.9f;\n\naudio.playEvent(explosion);\n

    "},{"location":"api_reference/audio/audio_engine/#void-setmastervolumefloat-volume","title":"void setMasterVolume(float volume)","text":"

    Sets the master volume for all audio output.

    Parameters: - volume (float): Volume level (0.0 = silent, 1.0 = full volume)

    Returns: - void

    Notes: - Affects all channels and events - Clamped to [0.0, 1.0] range - Use for volume control menus or mute functionality

    Example:

    auto& audio = engine.getAudioEngine();\naudio.setMasterVolume(0.5f);  // 50% volume\naudio.setMasterVolume(0.0f);  // Mute\naudio.setMasterVolume(1.0f);  // Full volume\n

    "},{"location":"api_reference/audio/audio_engine/#float-getmastervolume-const","title":"float getMasterVolume() const","text":"

    Gets the current master volume.

    Returns: - float: Current master volume (0.0 to 1.0)

    Example:

    float currentVolume = audioEngine.getMasterVolume();\n

    "},{"location":"api_reference/audio/audio_engine/#audio-channels","title":"Audio Channels","text":"

    The engine manages 4 fixed channels:

    1. Channel 0: Pulse wave
    2. Channel 1: Pulse wave
    3. Channel 2: Triangle wave
    4. Channel 3: Noise wave

    Notes: - Channels are automatically allocated when playing events - If all channels of a type are busy, new events may be dropped - Background music typically uses one channel (via MusicPlayer)

    "},{"location":"api_reference/audio/audio_engine/#usage-example","title":"Usage Example","text":"
    #include \"audio/AudioEngine.h\"\n#include \"audio/AudioConfig.h\"\n\nclass MyScene : public pixelroot32::core::Scene {\nprivate:\n    void playJumpSound() {\n        auto& audio = engine.getAudioEngine();\n\n        pixelroot32::audio::AudioEvent sound{};\n        sound.type = pixelroot32::audio::WaveType::PULSE;\n        sound.frequency = 800.0f;\n        sound.duration = 0.1f;\n        sound.volume = 0.7f;\n        sound.duty = 0.5f;\n\n        audio.playEvent(sound);\n    }\n\n    void playHitSound() {\n        auto& audio = engine.getAudioEngine();\n\n        pixelroot32::audio::AudioEvent sound{};\n        sound.type = pixelroot32::audio::WaveType::NOISE;\n        sound.frequency = 500.0f;\n        sound.duration = 0.05f;\n        sound.volume = 0.5f;\n\n        audio.playEvent(sound);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Audio is updated automatically by Engine\n        // Just play events when needed\n        if (playerJumped) {\n            playJumpSound();\n            playerJumped = false;\n        }\n    }\n};\n
    "},{"location":"api_reference/audio/audio_engine/#performance-considerations","title":"Performance Considerations","text":"
    • Channel limit: Only 4 channels total; plan sound effects accordingly
    • Event dropping: If all channels are busy, new events are silently dropped
    • Update frequency: update() must be called every frame for proper timing
    • Sample generation: generateSamples() is called by backend at audio rate (not game rate)
    "},{"location":"api_reference/audio/audio_engine/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Sample rate: Lower sample rates (11025 Hz) use less CPU and memory
    • Backend choice: DAC backend is simpler but lower quality than I2S
    • Buffer size: Larger buffers reduce underruns but increase latency
    • Channel management: Limit simultaneous sounds to avoid channel conflicts
    "},{"location":"api_reference/audio/audio_engine/#see-also","title":"See Also","text":"
    • AudioConfig - Audio configuration
    • AudioTypes - Audio data structures
    • MusicPlayer - Background music playback
    • Manual - Audio
    • API Overview
    "},{"location":"api_reference/audio/audio_types/","title":"Audio Types","text":"

    Data structures and types for the audio system.

    "},{"location":"api_reference/audio/audio_types/#description","title":"Description","text":"

    This document describes the data structures used by the audio system, including wave types, audio events, and channel state.

    "},{"location":"api_reference/audio/audio_types/#namespace","title":"Namespace","text":"
    namespace pixelroot32::audio {\n    // Types and structures\n}\n
    "},{"location":"api_reference/audio/audio_types/#wavetype-enum","title":"WaveType Enum","text":"

    Defines the types of waveforms available.

    Values: - WaveType::PULSE: Pulse wave (square wave with variable duty cycle) - WaveType::TRIANGLE: Triangle wave (smooth, melodic) - WaveType::NOISE: Noise wave (random, percussive)

    Example:

    pixelroot32::audio::WaveType wave = pixelroot32::audio::WaveType::PULSE;\n

    "},{"location":"api_reference/audio/audio_types/#audioevent-structure","title":"AudioEvent Structure","text":"

    A fire-and-forget sound event triggered by the game.

    Members: - WaveType type: Type of waveform to use - float frequency: Frequency in Hz - float duration: Duration in seconds - float volume: Volume level (0.0 to 1.0) - float duty: Duty cycle for pulse wave (0.0 to 1.0, pulse only)

    Example:

    pixelroot32::audio::AudioEvent jumpSound{};\njumpSound.type = pixelroot32::audio::WaveType::PULSE;\njumpSound.frequency = 800.0f;\njumpSound.duration = 0.1f;\njumpSound.volume = 0.7f;\njumpSound.duty = 0.5f;\n\nauto& audio = engine.getAudioEngine();\naudio.playEvent(jumpSound);\n

    "},{"location":"api_reference/audio/audio_types/#common-sound-effects","title":"Common Sound Effects","text":"

    Jump Sound:

    pixelroot32::audio::AudioEvent jump{};\njump.type = pixelroot32::audio::WaveType::PULSE;\njump.frequency = 800.0f;\njump.duration = 0.1f;\njump.volume = 0.7f;\njump.duty = 0.5f;\n

    Hit Sound:

    pixelroot32::audio::AudioEvent hit{};\nhit.type = pixelroot32::audio::WaveType::NOISE;\nhit.frequency = 500.0f;\nhit.duration = 0.05f;\nhit.volume = 0.5f;\n

    Collect Sound:

    pixelroot32::audio::AudioEvent collect{};\ncollect.type = pixelroot32::audio::WaveType::TRIANGLE;\ncollect.frequency = 1000.0f;\ncollect.duration = 0.15f;\ncollect.volume = 0.6f;\n

    Explosion:

    pixelroot32::audio::AudioEvent explosion{};\nexplosion.type = pixelroot32::audio::WaveType::NOISE;\nexplosion.frequency = 200.0f;\nexplosion.duration = 0.3f;\nexplosion.volume = 0.9f;\n

    "},{"location":"api_reference/audio/audio_types/#audiochannel-structure","title":"AudioChannel Structure","text":"

    Represents the internal state of a single audio channel.

    Members: - bool enabled: Whether the channel is active - WaveType type: Type of waveform - float frequency: Current frequency in Hz - float phase: Current phase (0.0 to 1.0) - float phaseIncrement: Pre-calculated phase increment - float volume: Current volume (0.0 to 1.0) - float targetVolume: Target volume for envelopes - float dutyCycle: Duty cycle for pulse wave (0.0 to 1.0) - uint16_t noiseRegister: LFSR state for noise generation - unsigned long durationMs: Total duration in milliseconds - unsigned long remainingMs: Remaining duration in milliseconds

    Methods: - void reset(): Resets the channel to inactive state

    Notes: - Internal structure, typically not accessed directly - Managed automatically by AudioEngine - 4 channels total: 2 Pulse, 1 Triangle, 1 Noise

    "},{"location":"api_reference/audio/audio_types/#frequency-reference","title":"Frequency Reference","text":"

    Common frequencies for musical notes (A4 = 440 Hz):

    • C4: 261.63 Hz
    • D4: 293.66 Hz
    • E4: 329.63 Hz
    • F4: 349.23 Hz
    • G4: 392.00 Hz
    • A4: 440.00 Hz
    • B4: 493.88 Hz
    • C5: 523.25 Hz

    Example:

    // Play a C note\npixelroot32::audio::AudioEvent note{};\nnote.type = pixelroot32::audio::WaveType::PULSE;\nnote.frequency = 261.63f;  // C4\nnote.duration = 0.5f;\nnote.volume = 0.8f;\nnote.duty = 0.5f;\n

    "},{"location":"api_reference/audio/audio_types/#duty-cycle-pulse-wave","title":"Duty Cycle (Pulse Wave)","text":"

    Duty cycle controls the shape of the pulse wave:

    • 0.125 (12.5%): Thin pulse (NES-like)
    • 0.25 (25%): Narrow pulse
    • 0.5 (50%): Square wave (most common)
    • 0.75 (75%): Wide pulse

    Example:

    // Thin pulse (NES style)\nevent.duty = 0.125f;\n\n// Square wave (standard)\nevent.duty = 0.5f;\n

    "},{"location":"api_reference/audio/audio_types/#usage-examples","title":"Usage Examples","text":""},{"location":"api_reference/audio/audio_types/#creating-sound-effect-library","title":"Creating Sound Effect Library","text":"
    namespace SoundEffects {\n    // Jump sound\n    inline pixelroot32::audio::AudioEvent jump() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::PULSE;\n        evt.frequency = 800.0f;\n        evt.duration = 0.1f;\n        evt.volume = 0.7f;\n        evt.duty = 0.5f;\n        return evt;\n    }\n\n    // Hit sound\n    inline pixelroot32::audio::AudioEvent hit() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::NOISE;\n        evt.frequency = 500.0f;\n        evt.duration = 0.05f;\n        evt.volume = 0.5f;\n        return evt;\n    }\n\n    // Collect sound\n    inline pixelroot32::audio::AudioEvent collect() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::TRIANGLE;\n        evt.frequency = 1000.0f;\n        evt.duration = 0.15f;\n        evt.volume = 0.6f;\n        return evt;\n    }\n}\n\n// Usage\nauto& audio = engine.getAudioEngine();\naudio.playEvent(SoundEffects::jump());\naudio.playEvent(SoundEffects::hit());\n
    "},{"location":"api_reference/audio/audio_types/#frequency-sweep-effect","title":"Frequency Sweep Effect","text":"
    void playSweepSound() {\n    auto& audio = engine.getAudioEngine();\n\n    // Create multiple events for sweep effect\n    for (int i = 0; i < 5; i++) {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::PULSE;\n        evt.frequency = 400.0f + (i * 200.0f);  // Sweep from 400 to 1200 Hz\n        evt.duration = 0.05f;\n        evt.volume = 0.6f;\n        evt.duty = 0.5f;\n\n        audio.playEvent(evt);\n        delay(50);  // Small delay between events\n    }\n}\n
    "},{"location":"api_reference/audio/audio_types/#performance-considerations","title":"Performance Considerations","text":"
    • Event creation: Creating events is fast (just struct initialization)
    • Channel allocation: Events are queued and played when channels are available
    • Frequency range: Keep frequencies in reasonable range (100-5000 Hz) for best results
    • Duration: Shorter durations free channels faster
    "},{"location":"api_reference/audio/audio_types/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Events are small structs, safe to create frequently
    • CPU: Audio generation is efficient but limit simultaneous sounds
    • Quality: Lower sample rates reduce CPU usage
    "},{"location":"api_reference/audio/audio_types/#see-also","title":"See Also","text":"
    • AudioEngine - Audio playback engine
    • AudioConfig - Audio configuration
    • Manual - Audio
    • API Overview
    "},{"location":"api_reference/audio/music_player/","title":"MusicPlayer","text":"

    Lightweight sequencer built on top of AudioEngine to play background melodies as tracks.

    "},{"location":"api_reference/audio/music_player/#description","title":"Description","text":"

    MusicPlayer is a simple sequencer that plays MusicTrack structures. It advances notes based on game time, converts MusicNote entries to AudioEvent calls, and manages playback state (play, stop, pause, resume).

    The player uses one audio channel (typically a Pulse channel) for music, leaving other channels available for sound effects.

    "},{"location":"api_reference/audio/music_player/#namespace","title":"Namespace","text":"
    namespace pixelroot32::audio {\n    class MusicPlayer {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/audio/music_player/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Engine (manages music player instance)
    "},{"location":"api_reference/audio/music_player/#constructors","title":"Constructors","text":""},{"location":"api_reference/audio/music_player/#musicplayeraudioengine-engine","title":"MusicPlayer(AudioEngine& engine)","text":"

    Constructs the MusicPlayer.

    Parameters: - engine (AudioEngine&): Reference to the AudioEngine used to play sounds

    Notes: - Typically created and managed by Engine - Access via engine.getMusicPlayer()

    Example:

    auto& audio = engine.getAudioEngine();\npixelroot32::audio::MusicPlayer musicPlayer(audio);\n

    "},{"location":"api_reference/audio/music_player/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/audio/music_player/#void-playconst-musictrack-track","title":"void play(const MusicTrack& track)","text":"

    Starts playing a track.

    Parameters: - track (const MusicTrack&): The track to play

    Returns: - void

    Notes: - Stops any currently playing track - Starts from the beginning of the track - If track has loop = true, will loop automatically - Uses one audio channel (typically Pulse)

    Example:

    static const MusicNote MELODY[] = {\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::E, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::G, 0.25f),\n    makeRest(0.10f),\n};\n\nstatic const MusicTrack GAME_MUSIC = {\n    MELODY,\n    sizeof(MELODY) / sizeof(MusicNote),\n    true,  // loop\n    WaveType::PULSE,\n    0.5f   // volume\n};\n\nvoid init() override {\n    auto& music = engine.getMusicPlayer();\n    music.play(GAME_MUSIC);\n}\n

    "},{"location":"api_reference/audio/music_player/#void-stop","title":"void stop()","text":"

    Stops playback and silences the channel.

    Returns: - void

    Notes: - Immediately stops the current note - Resets playback to the beginning - Channel is freed for other use

    Example:

    void onGameOver() {\n    auto& music = engine.getMusicPlayer();\n    music.stop();\n}\n

    "},{"location":"api_reference/audio/music_player/#void-pause","title":"void pause()","text":"

    Pauses playback.

    Returns: - void

    Notes: - Current note continues until it ends, then playback pauses - Playback state is preserved (can resume from where it paused) - Use for pause menus

    Example:

    void onPause() {\n    auto& music = engine.getMusicPlayer();\n    music.pause();\n}\n

    "},{"location":"api_reference/audio/music_player/#void-resume","title":"void resume()","text":"

    Resumes playback.

    Returns: - void

    Notes: - Only works if playback was paused - Resumes from where it was paused - Use to unpause after pause menu

    Example:

    void onResume() {\n    auto& music = engine.getMusicPlayer();\n    music.resume();\n}\n

    "},{"location":"api_reference/audio/music_player/#void-updateunsigned-long-deltatime","title":"void update(unsigned long deltaTime)","text":"

    Updates the player state. Should be called every frame.

    Parameters: - deltaTime (unsigned long): Time elapsed since last frame in milliseconds

    Returns: - void

    Notes: - Must be called every frame for proper timing - Advances note playback based on elapsed time - Automatically plays next notes in sequence - Typically called automatically by Engine

    Example:

    // Called automatically by Engine, but can be called manually:\nvoid update(unsigned long deltaTime) override {\n    Scene::update(deltaTime);\n\n    // Music player is updated automatically by Engine\n    // No need to call manually\n}\n

    "},{"location":"api_reference/audio/music_player/#bool-isplaying-const","title":"bool isPlaying() const","text":"

    Checks if a track is currently playing.

    Returns: - bool: true if playing, false otherwise

    Notes: - Returns false if stopped or paused - Use to check playback state before operations

    Example:

    auto& music = engine.getMusicPlayer();\nif (music.isPlaying()) {\n    // Music is active\n} else {\n    // Music is stopped or paused\n}\n

    "},{"location":"api_reference/audio/music_player/#void-settempofactorfloat-factor","title":"void setTempoFactor(float factor)","text":"

    Sets the global tempo scaling factor.

    Parameters: - factor (float): Tempo multiplier - 1.0f: Normal speed - 2.0f: Double speed - 0.5f: Half speed

    Returns: - void

    Notes: - Affects all note durations - Useful for speed-up effects or slow-motion - Applied to all tracks

    Example:

    auto& music = engine.getMusicPlayer();\nmusic.setTempoFactor(1.5f);  // 50% faster\nmusic.setTempoFactor(0.5f);   // 50% slower\nmusic.setTempoFactor(1.0f);   // Normal speed\n

    "},{"location":"api_reference/audio/music_player/#float-gettempofactor-const","title":"float getTempoFactor() const","text":"

    Gets the current tempo scaling factor.

    Returns: - float: Current factor (default 1.0f)

    Example:

    float currentTempo = musicPlayer.getTempoFactor();\n

    "},{"location":"api_reference/audio/music_player/#musictrack-structure","title":"MusicTrack Structure","text":"

    A MusicTrack contains:

    • notes (const MusicNote*): Array of music notes
    • noteCount (size_t): Number of notes in the array
    • loop (bool): Whether to loop the track
    • waveType (WaveType): Wave type to use (typically PULSE)
    • volume (float): Volume level (0.0 to 1.0)
    "},{"location":"api_reference/audio/music_player/#musicnote-structure","title":"MusicNote Structure","text":"

    A MusicNote contains:

    • instrument (InstrumentPreset): Instrument preset to use
    • note (Note): Musical note (C, D, E, etc.)
    • duration (float): Duration in seconds

    Use helper functions: - makeNote(instrument, note, duration): Create a note - makeRest(duration): Create a rest (silence)

    "},{"location":"api_reference/audio/music_player/#usage-example","title":"Usage Example","text":"
    #include \"audio/MusicPlayer.h\"\n#include \"audio/AudioMusicTypes.h\"\n\nusing namespace pixelroot32::audio;\n\n// Define a simple melody\nstatic const MusicNote MAIN_THEME[] = {\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.25f),\n    makeNote(INSTR_PULSE_LEAD, Note::E, 0.25f),\n    makeNote(INSTR_PULSE_LEAD, Note::G, 0.25f),\n    makeRest(0.1f),\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.5f),\n    makeRest(0.2f),\n};\n\nstatic const MusicTrack MAIN_THEME_TRACK = {\n    MAIN_THEME,\n    sizeof(MAIN_THEME) / sizeof(MusicNote),\n    true,           // loop\n    WaveType::PULSE,\n    0.6f            // volume\n};\n\nclass GameScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Start background music\n        auto& music = engine.getMusicPlayer();\n        music.play(MAIN_THEME_TRACK);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Music updates automatically\n    }\n\n    void onPauseMenu() {\n        auto& music = engine.getMusicPlayer();\n        music.pause();\n    }\n\n    void onResumeGame() {\n        auto& music = engine.getMusicPlayer();\n        music.resume();\n    }\n\n    void onGameOver() {\n        auto& music = engine.getMusicPlayer();\n        music.stop();\n    }\n};\n
    "},{"location":"api_reference/audio/music_player/#performance-considerations","title":"Performance Considerations","text":"
    • One channel: Music uses one channel, leaving others for sound effects
    • Update frequency: update() must be called every frame
    • Track size: Larger tracks use more memory (store in flash)
    • Tempo factor: Changing tempo is fast (just a multiplier)
    "},{"location":"api_reference/audio/music_player/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Store tracks in flash (const/constexpr) to save RAM
    • CPU: Music playback is lightweight (simple sequencing)
    • Channel conflict: Music and sound effects share channels; plan accordingly
    "},{"location":"api_reference/audio/music_player/#see-also","title":"See Also","text":"
    • AudioEngine - Audio playback engine
    • AudioTypes - Audio data structures
    • AudioMusicTypes - Music data structures
    • Manual - Audio
    • API Overview
    "},{"location":"api_reference/core/actor/","title":"Actor","text":"

    An Entity capable of physical interaction and collision.

    "},{"location":"api_reference/core/actor/#description","title":"Description","text":"

    Actor extends Entity with collision layers and masks. Actors are used for dynamic game objects like players, enemies, projectiles, and obstacles that need to interact with each other through collision detection.

    Actors participate in the collision system and can detect collisions with other actors based on their collision layers and masks.

    "},{"location":"api_reference/core/actor/#namespace","title":"Namespace","text":"
    namespace pixelroot32::core {\n    class Actor : public Entity {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/actor/#inheritance","title":"Inheritance","text":"
    • Inherits from: Entity
    • Inherited by: PhysicsActor and your custom actor classes
    "},{"location":"api_reference/core/actor/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/actor/#actorfloat-x-float-y-int-w-int-h","title":"Actor(float x, float y, int w, int h)","text":"

    Creates a new actor with specified position and size.

    Parameters: - x (float): Initial X position in world space - y (float): Initial Y position in world space - w (int): Actor width in pixels - h (int): Actor height in pixels

    Notes: - Actor type is automatically set to EntityType::ACTOR - Collision layer and mask default to DefaultLayers::kNone - Must set collision layer and mask for collision detection to work

    Example:

    class PlayerActor : public pixelroot32::core::Actor {\npublic:\n    PlayerActor(float x, float y) \n        : Actor(x, y, 16, 16) {\n        // Set collision layer and mask\n        layer = pixelroot32::physics::DefaultLayers::kPlayer;\n        mask = pixelroot32::physics::DefaultLayers::kEnemy | \n               pixelroot32::physics::DefaultLayers::kObstacle;\n    }\n\n    void update(unsigned long deltaTime) override {\n        Actor::update(deltaTime);\n        // Player logic\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawSprite(playerSprite, \n                           static_cast<int>(x), \n                           static_cast<int>(y), \n                           Color::White);\n    }\n\n    Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(Actor* other) override {\n        // Handle collision\n    }\n};\n

    "},{"location":"api_reference/core/actor/#public-properties","title":"Public Properties","text":""},{"location":"api_reference/core/actor/#collisionlayer-layer","title":"CollisionLayer layer","text":"

    The collision layer this actor belongs to.

    Type: pixelroot32::physics::CollisionLayer (uint16_t)

    Access: Read-write

    Default: DefaultLayers::kNone

    Notes: - Defines which layer this actor is on - Use bit flags to assign multiple layers (e.g., kPlayer | kProjectile) - Only actors with matching layers in their mask will collide

    Example:

    actor->layer = pixelroot32::physics::DefaultLayers::kPlayer;\n

    "},{"location":"api_reference/core/actor/#collisionlayer-mask","title":"CollisionLayer mask","text":"

    The collision layers this actor interacts with.

    Type: pixelroot32::physics::CollisionLayer (uint16_t)

    Access: Read-write

    Default: DefaultLayers::kNone

    Notes: - Defines which layers this actor can collide with - Use bit flags to check multiple layers (e.g., kEnemy | kObstacle) - Collision only occurs if the other actor's layer matches bits in this mask

    Example:

    // Actor collides with enemies and obstacles\nactor->mask = pixelroot32::physics::DefaultLayers::kEnemy | \n              pixelroot32::physics::DefaultLayers::kObstacle;\n

    "},{"location":"api_reference/core/actor/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/actor/#void-setcollisionlayercollisionlayer-l","title":"void setCollisionLayer(CollisionLayer l)","text":"

    Sets the collision layer for this actor.

    Parameters: - l (pixelroot32::physics::CollisionLayer): The layer to set

    Returns: - void

    Notes: - Equivalent to setting layer directly - Use bit flags for multiple layers

    Example:

    actor->setCollisionLayer(pixelroot32::physics::DefaultLayers::kPlayer);\n

    "},{"location":"api_reference/core/actor/#void-setcollisionmaskcollisionlayer-m","title":"void setCollisionMask(CollisionLayer m)","text":"

    Sets the collision mask for this actor.

    Parameters: - m (pixelroot32::physics::CollisionLayer): The mask to set

    Returns: - void

    Notes: - Equivalent to setting mask directly - Use bit flags for multiple layers

    Example:

    actor->setCollisionMask(pixelroot32::physics::DefaultLayers::kEnemy | \n                        pixelroot32::physics::DefaultLayers::kObstacle);\n

    "},{"location":"api_reference/core/actor/#bool-isinlayeruint16_t-targetlayer-const","title":"bool isInLayer(uint16_t targetLayer) const","text":"

    Checks if the Actor belongs to a specific collision layer.

    Parameters: - targetLayer (uint16_t): The bit(s) to check (e.g., DefaultLayers::kPlayer)

    Returns: - bool: true if the bit is set in the actor's layer

    Notes: - Uses bitwise AND operation - Useful for checking if an actor is on a specific layer

    Example:

    if (actor->isInLayer(pixelroot32::physics::DefaultLayers::kPlayer)) {\n    // This is a player actor\n}\n

    "},{"location":"api_reference/core/actor/#virtual-rect-gethitbox-0","title":"virtual Rect getHitBox() = 0","text":"

    Gets the hitbox for collision detection. Must be implemented by derived classes.

    Returns: - Rect: A rectangle representing the collision bounds

    Notes: - Called by the collision system to check collisions - Should return the actual collision bounds (may differ from visual size) - Use AABB (Axis-Aligned Bounding Box) for efficiency

    Example:

    Rect getHitBox() override {\n    // Return collision bounds (may be smaller than visual)\n    return {x + 2, y + 2, width - 4, height - 4};\n}\n

    "},{"location":"api_reference/core/actor/#virtual-void-oncollisionactor-other-0","title":"virtual void onCollision(Actor* other) = 0","text":"

    Callback invoked when a collision occurs. Must be implemented by derived classes.

    Parameters: - other (Actor*): The actor that this actor collided with

    Notes: - Called automatically by the collision system when a collision is detected - Both actors' onCollision() methods are called - Use to handle collision responses (damage, bouncing, etc.)

    Example:

    void onCollision(Actor* other) override {\n    // Check what we collided with\n    if (other->isInLayer(pixelroot32::physics::DefaultLayers::kEnemy)) {\n        // Take damage\n        health--;\n        if (health <= 0) {\n            isEnabled = false;\n        }\n    } else if (other->isInLayer(pixelroot32::physics::DefaultLayers::kCollectible)) {\n        // Collect item\n        score += 10;\n        other->isEnabled = false;  // Remove collectible\n    }\n}\n

    "},{"location":"api_reference/core/actor/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the actor logic. Default implementation does nothing.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    Notes: - Override to implement actor-specific update logic - Called automatically by Scene if isEnabled is true - Use deltaTime for frame-rate independent movement

    Example:

    void update(unsigned long deltaTime) override {\n    Actor::update(deltaTime);  // Call base implementation\n\n    // Move actor\n    float speed = 100.0f;  // pixels per second\n    x += (speed * deltaTime) / 1000.0f;\n}\n

    "},{"location":"api_reference/core/actor/#collision-layers","title":"Collision Layers","text":"

    Collision layers use bit flags to organize actors into groups. Common layers:

    • DefaultLayers::kNone (0): No layer
    • DefaultLayers::kPlayer (1 << 0): Player actors
    • DefaultLayers::kEnemy (1 << 1): Enemy actors
    • DefaultLayers::kObstacle (1 << 2): Obstacles/walls
    • DefaultLayers::kProjectile (1 << 3): Projectiles
    • DefaultLayers::kCollectible (1 << 4): Collectible items

    Example:

    // Player collides with enemies and obstacles\nplayer->layer = DefaultLayers::kPlayer;\nplayer->mask = DefaultLayers::kEnemy | DefaultLayers::kObstacle;\n\n// Enemy collides with player and obstacles\nenemy->layer = DefaultLayers::kEnemy;\nenemy->mask = DefaultLayers::kPlayer | DefaultLayers::kObstacle;\n\n// Projectile collides with enemies\nprojectile->layer = DefaultLayers::kProjectile;\nprojectile->mask = DefaultLayers::kEnemy;\n

    "},{"location":"api_reference/core/actor/#usage-example","title":"Usage Example","text":"
    #include \"core/Actor.h\"\n#include \"physics/CollisionTypes.h\"\n\nclass EnemyActor : public pixelroot32::core::Actor {\nprivate:\n    const pixelroot32::graphics::Sprite* sprite;\n    int health = 3;\n\npublic:\n    EnemyActor(float x, float y) \n        : Actor(x, y, 16, 16),\n          sprite(&enemySprite) {\n        // Set collision layer and mask\n        layer = pixelroot32::physics::DefaultLayers::kEnemy;\n        mask = pixelroot32::physics::DefaultLayers::kPlayer | \n               pixelroot32::physics::DefaultLayers::kProjectile;\n    }\n\n    void update(unsigned long deltaTime) override {\n        Actor::update(deltaTime);\n\n        // Move towards player\n        float speed = 50.0f;\n        // ... movement logic\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawSprite(*sprite, \n                           static_cast<int>(x), \n                           static_cast<int>(y), \n                           Color::Red);\n    }\n\n    Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(Actor* other) override {\n        if (other->isInLayer(pixelroot32::physics::DefaultLayers::kProjectile)) {\n            // Hit by projectile\n            health--;\n            if (health <= 0) {\n                isEnabled = false;  // Remove enemy\n            }\n        }\n    }\n};\n
    "},{"location":"api_reference/core/actor/#performance-considerations","title":"Performance Considerations","text":"
    • Collision layers: Use layers efficiently to reduce collision checks
    • Hitbox size: Keep hitboxes simple (AABB) for best performance
    • Collision callbacks: Keep onCollision() fast; avoid expensive operations
    • Layer organization: Group actors by layer to minimize checks
    "},{"location":"api_reference/core/actor/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Collision checks: Collision system automatically optimizes using layers
    • Memory: Each actor consumes memory; stay within MAX_ENTITIES limit
    • Object pooling: Reuse actors instead of creating/destroying frequently
    "},{"location":"api_reference/core/actor/#see-also","title":"See Also","text":"
    • Entity - Base entity class
    • PhysicsActor - Entity with physics
    • CollisionSystem - Collision detection
    • CollisionTypes - Collision layer definitions
    • Manual - Scenes and Entities
    • Manual - Physics and Collisions
    • API Overview
    "},{"location":"api_reference/core/engine/","title":"Engine","text":"

    The main engine class that manages the game loop and core subsystems.

    "},{"location":"api_reference/core/engine/#description","title":"Description","text":"

    Engine acts as the central hub of the PixelRoot32 game engine. It initializes and manages the Renderer, InputManager, AudioEngine, and SceneManager. It runs the main game loop, handling timing (delta time), updating the current scene, and rendering frames.

    The engine provides a unified interface for both ESP32 and Native (SDL2) platforms, abstracting platform-specific details while maintaining consistent behavior.

    "},{"location":"api_reference/core/engine/#namespace","title":"Namespace","text":"
    namespace pixelroot32::core {\n    class Engine {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/engine/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Application entry point (main.cpp)
    "},{"location":"api_reference/core/engine/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/engine/#engineconst-displayconfig-displayconfig-const-inputconfig-inputconfig-const-audioconfig-audioconfig","title":"Engine(const DisplayConfig& displayConfig, const InputConfig& inputConfig, const AudioConfig& audioConfig)","text":"

    Creates a new engine instance with custom display, input, and audio configurations.

    Parameters: - displayConfig (const pixelroot32::graphics::DisplayConfig&): Configuration settings for the display (width, height, rotation, etc.) - inputConfig (const pixelroot32::input::InputConfig&): Configuration settings for the input system (pins, buttons) - audioConfig (const pixelroot32::audio::AudioConfig&): Configuration settings for the audio system (backend, sample rate, buffer size)

    Example:

    #include \"core/Engine.h\"\n#include \"graphics/DisplayConfig.h\"\n#include \"input/InputConfig.h\"\n#include \"audio/AudioConfig.h\"\n\npixelroot32::graphics::DisplayConfig displayConfig;\ndisplayConfig.width = 128;\ndisplayConfig.height = 128;\n\npixelroot32::input::InputConfig inputConfig;\n// Configure input pins...\n\npixelroot32::audio::AudioConfig audioConfig;\naudioConfig.backend = pixelroot32::audio::AudioConfig::Backend::ESP32_DAC;\naudioConfig.sampleRate = 11025;\n\npixelroot32::core::Engine engine(displayConfig, inputConfig, audioConfig);\nengine.init();\nengine.run();\n

    "},{"location":"api_reference/core/engine/#engineconst-displayconfig-displayconfig-const-inputconfig-inputconfig","title":"Engine(const DisplayConfig& displayConfig, const InputConfig& inputConfig)","text":"

    Creates a new engine instance with custom display and input configurations, using default audio settings.

    Parameters: - displayConfig (const pixelroot32::graphics::DisplayConfig&): Configuration settings for the display - inputConfig (const pixelroot32::input::InputConfig&): Configuration settings for the input system

    Example:

    pixelroot32::core::Engine engine(displayConfig, inputConfig);\nengine.init();\nengine.run();\n

    "},{"location":"api_reference/core/engine/#engineconst-displayconfig-displayconfig","title":"Engine(const DisplayConfig& displayConfig)","text":"

    Creates a new engine instance with custom display configuration and default input/audio settings.

    Parameters: - displayConfig (const pixelroot32::graphics::DisplayConfig&): Configuration settings for the display

    Example:

    pixelroot32::core::Engine engine(displayConfig);\nengine.init();\nengine.run();\n

    "},{"location":"api_reference/core/engine/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/engine/#void-init","title":"void init()","text":"

    Initializes the engine subsystems. This method must be called before run().

    Returns: - void

    Notes: - Initializes the Renderer, InputManager, and sets up the initial state - Must be called after construction and before run() - Safe to call multiple times (idempotent)

    Example:

    Engine engine(displayConfig);\nengine.init();  // Initialize subsystems\nengine.setScene(myScene);\nengine.run();   // Start game loop\n

    "},{"location":"api_reference/core/engine/#void-run","title":"void run()","text":"

    Starts the main game loop. This method contains the infinite loop that calls update() and draw() repeatedly until the application exits.

    Returns: - void

    Notes: - This method blocks until the application exits - Handles frame timing and delta time calculation automatically - Calls update() and draw() once per frame - Do not call this method multiple times

    Example:

    Engine engine(displayConfig);\nengine.init();\nengine.setScene(myScene);\nengine.run();  // Blocks here, runs game loop\n

    "},{"location":"api_reference/core/engine/#unsigned-long-getdeltatime-const","title":"unsigned long getDeltaTime() const","text":"

    Gets the time elapsed since the last frame.

    Returns: - unsigned long: The delta time in milliseconds

    Performance Notes: - Very fast (inline accessor) - Safe to call every frame - Use this value to make movement frame-rate independent

    Example:

    void update(unsigned long deltaTime) override {\n    auto& engine = getEngine();\n    unsigned long dt = engine.getDeltaTime();\n\n    // Move at constant speed regardless of FPS\n    float speed = 100.0f;  // pixels per second\n    x += (speed * dt) / 1000.0f;\n}\n

    "},{"location":"api_reference/core/engine/#void-setscenescene-newscene","title":"void setScene(Scene* newScene)","text":"

    Sets the current active scene to be updated and rendered.

    Parameters: - newScene (Scene*): Pointer to the new Scene to become active. Can be nullptr to clear the current scene.

    Notes: - The previous scene is replaced (not pushed onto a stack) - Use SceneManager for push/pop operations if needed - The scene's init() method will be called automatically - Safe to call during the game loop

    Example:

    class MainMenuScene : public pixelroot32::core::Scene {\n    // ...\n};\n\nclass GameScene : public pixelroot32::core::Scene {\n    // ...\n};\n\nMainMenuScene menuScene;\nGameScene gameScene;\n\nEngine engine(displayConfig);\nengine.init();\nengine.setScene(&menuScene);  // Start with menu\nengine.run();\n

    "},{"location":"api_reference/core/engine/#scene-getcurrentscene-const","title":"Scene* getCurrentScene() const","text":"

    Retrieves the currently active scene.

    Returns: - Scene*: Pointer to the current Scene, or nullptr if none is set

    Example:

    auto* currentScene = engine.getCurrentScene();\nif (currentScene) {\n    // Scene is active\n}\n

    "},{"location":"api_reference/core/engine/#void-setrendererrenderer-newrenderer","title":"void setRenderer(Renderer& newRenderer)","text":"

    Replaces the current renderer instance.

    Parameters: - newRenderer (pixelroot32::graphics::Renderer&): Reference to the new Renderer to use

    Notes: - Advanced usage: typically not needed unless implementing custom renderer - The renderer must be properly initialized before use - Use with caution: may break existing rendering code

    "},{"location":"api_reference/core/engine/#renderer-getrenderer","title":"Renderer& getRenderer()","text":"

    Provides access to the Renderer subsystem.

    Returns: - pixelroot32::graphics::Renderer&: Reference to the current Renderer

    Example:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    auto& engineRenderer = engine.getRenderer();\n    engineRenderer.drawSprite(mySprite, 100, 100, Color::White);\n}\n

    "},{"location":"api_reference/core/engine/#inputmanager-getinputmanager","title":"InputManager& getInputManager()","text":"

    Provides access to the InputManager subsystem.

    Returns: - pixelroot32::input::InputManager&: Reference to the InputManager

    Example:

    void update(unsigned long deltaTime) override {\n    auto& input = engine.getInputManager();\n    if (input.isButtonPressed(Buttons::A)) {\n        // Handle button press\n    }\n}\n

    "},{"location":"api_reference/core/engine/#audioengine-getaudioengine","title":"AudioEngine& getAudioEngine()","text":"

    Provides access to the AudioEngine subsystem.

    Returns: - pixelroot32::audio::AudioEngine&: Reference to the AudioEngine

    Example:

    void playSound() {\n    auto& audio = engine.getAudioEngine();\n    pixelroot32::audio::AudioEvent sound{};\n    sound.type = pixelroot32::audio::WaveType::PULSE;\n    sound.frequency = 800.0f;\n    sound.duration = 0.1f;\n    audio.playEvent(sound);\n}\n

    "},{"location":"api_reference/core/engine/#musicplayer-getmusicplayer","title":"MusicPlayer& getMusicPlayer()","text":"

    Provides access to the MusicPlayer subsystem.

    Returns: - pixelroot32::audio::MusicPlayer&: Reference to the MusicPlayer

    Example:

    void playMusic() {\n    auto& music = engine.getMusicPlayer();\n    music.playTrack(myMusicTrack);\n}\n

    "},{"location":"api_reference/core/engine/#usage-example","title":"Usage Example","text":"
    #include \"core/Engine.h\"\n#include \"graphics/DisplayConfig.h\"\n#include \"MyScene.h\"\n\nvoid setup() {\n    // Configure display\n    pixelroot32::graphics::DisplayConfig displayConfig;\n    displayConfig.width = 128;\n    displayConfig.height = 128;\n    displayConfig.rotation = 0;\n\n    // Create engine\n    pixelroot32::core::Engine engine(displayConfig);\n\n    // Initialize\n    engine.init();\n\n    // Create and set scene\n    MyScene myScene;\n    engine.setScene(&myScene);\n\n    // Run game loop\n    engine.run();\n}\n
    "},{"location":"api_reference/core/engine/#performance-considerations","title":"Performance Considerations","text":"
    • Initialization: init() should be called once at startup, not in the game loop
    • Scene switching: Switching scenes is fast but avoid doing it every frame
    • Subsystem access: Getters are inline and very fast; safe to call every frame
    • Delta time: Use getDeltaTime() for frame-rate independent movement
    "},{"location":"api_reference/core/engine/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Ensure init() completes before run() to avoid initialization issues
    • Monitor memory usage when switching scenes frequently
    • Use getDeltaTime() for consistent gameplay across different frame rates
    "},{"location":"api_reference/core/engine/#see-also","title":"See Also","text":"
    • Scene - Scene management
    • Renderer - Rendering system
    • InputManager - Input handling
    • AudioEngine - Audio system
    • Getting Started - Fundamental Concepts
    • Manual - Scenes and Entities
    • API Overview
    "},{"location":"api_reference/core/entity/","title":"Entity","text":"

    Abstract base class for all game objects.

    "},{"location":"api_reference/core/entity/#description","title":"Description","text":"

    Entity is the fundamental building block of the scene. Entities have a position, size, and lifecycle methods (update, draw). All game objects inherit from Entity, including actors, UI elements, and custom game objects.

    Entities are managed by Scene and are automatically updated and drawn each frame when enabled and visible.

    "},{"location":"api_reference/core/entity/#namespace","title":"Namespace","text":"
    namespace pixelroot32::core {\n    class Entity {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/entity/#inheritance","title":"Inheritance","text":"
    • Base class: None (abstract base class)
    • Inherited by: Actor, UI elements, and your custom entity classes
    "},{"location":"api_reference/core/entity/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/entity/#entityfloat-x-float-y-int-w-int-h-entitytype-t","title":"Entity(float x, float y, int w, int h, EntityType t)","text":"

    Creates a new entity with specified position, size, and type.

    Parameters: - x (float): Initial X position in world space - y (float): Initial Y position in world space - w (int): Width in pixels - h (int): Height in pixels - t (EntityType): The type of entity (GENERIC, ACTOR, UI_ELEMENT)

    Example:

    class MyEntity : public pixelroot32::core::Entity {\npublic:\n    MyEntity(float x, float y) \n        : Entity(x, y, 16, 16, EntityType::GENERIC) {}\n\n    void update(unsigned long deltaTime) override {\n        // Update logic\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw logic\n    }\n};\n

    "},{"location":"api_reference/core/entity/#public-properties","title":"Public Properties","text":""},{"location":"api_reference/core/entity/#float-x-y","title":"float x, y","text":"

    Position of the entity in world space.

    Type: float

    Access: Read-write

    Notes: - Position is in world coordinates, not screen coordinates - Can be modified directly or through helper methods - Use for entity positioning and movement

    Example:

    entity->x = 100.0f;\nentity->y = 50.0f;\n

    "},{"location":"api_reference/core/entity/#int-width-height","title":"int width, height","text":"

    Dimensions of the entity in pixels.

    Type: int

    Access: Read-write

    Notes: - Used for collision detection, rendering bounds, and layout - Should match the visual size of the entity - Can be modified at runtime if needed

    Example:

    entity->width = 32;\nentity->height = 32;\n

    "},{"location":"api_reference/core/entity/#entitytype-type","title":"EntityType type","text":"

    The specific type of this entity.

    Type: EntityType enum

    Access: Read-only (set in constructor)

    Values: - EntityType::GENERIC: Generic entity - EntityType::ACTOR: Actor entity (with collision) - EntityType::UI_ELEMENT: UI element

    Notes: - Used for type-safe casting and logic differentiation - Set once in constructor, typically not changed

    "},{"location":"api_reference/core/entity/#bool-isvisible","title":"bool isVisible","text":"

    If false, the entity's draw() method will not be called.

    Type: bool

    Access: Read-write

    Default: true

    Notes: - Use to hide entities without removing them from the scene - More efficient than removing and re-adding entities - Useful for object pooling

    Example:

    entity->isVisible = false;  // Hide entity\nentity->setVisible(true);   // Show entity\n

    "},{"location":"api_reference/core/entity/#bool-isenabled","title":"bool isEnabled","text":"

    If false, the entity's update() method will not be called.

    Type: bool

    Access: Read-write

    Default: true

    Notes: - Use to disable entity logic without removing it - Entity still exists but doesn't update - Useful for paused entities or object pooling

    Example:

    entity->isEnabled = false;  // Disable updates\nentity->setEnabled(true);   // Enable updates\n

    "},{"location":"api_reference/core/entity/#unsigned-char-renderlayer","title":"unsigned char renderLayer","text":"

    The render layer this entity is drawn on.

    Type: unsigned char

    Access: Read-write

    Default: 1

    Notes: - Layers are drawn in ascending order (0 = background, 1 = gameplay, 2 = UI) - Entities on the same layer are drawn in add order - Use to control draw order without changing entity order

    Example:

    entity->renderLayer = 0;  // Background layer\nentity->setRenderLayer(2); // UI layer\n

    "},{"location":"api_reference/core/entity/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/entity/#virtual-void-setvisiblebool-v","title":"virtual void setVisible(bool v)","text":"

    Sets the visibility of the entity.

    Parameters: - v (bool): true to show, false to hide

    Returns: - void

    Notes: - Equivalent to setting isVisible directly - Can be overridden for custom visibility logic

    Example:

    entity->setVisible(false);  // Hide\nentity->setVisible(true);   // Show\n

    "},{"location":"api_reference/core/entity/#virtual-void-setenabledbool-e","title":"virtual void setEnabled(bool e)","text":"

    Sets the enabled state of the entity.

    Parameters: - e (bool): true to enable, false to disable

    Returns: - void

    Notes: - Equivalent to setting isEnabled directly - Can be overridden for custom enable logic

    Example:

    entity->setEnabled(false);  // Disable updates\nentity->setEnabled(true);   // Enable updates\n

    "},{"location":"api_reference/core/entity/#unsigned-char-getrenderlayer-const","title":"unsigned char getRenderLayer() const","text":"

    Gets the current render layer.

    Returns: - unsigned char: The render layer (0-255)

    Example:

    unsigned char layer = entity->getRenderLayer();\n

    "},{"location":"api_reference/core/entity/#virtual-void-setrenderlayerunsigned-char-layer","title":"virtual void setRenderLayer(unsigned char layer)","text":"

    Sets the render layer for this entity.

    Parameters: - layer (unsigned char): The render layer (0 = background, 1 = gameplay, 2 = UI)

    Returns: - void

    Example:

    entity->setRenderLayer(0);  // Background\n

    "},{"location":"api_reference/core/entity/#virtual-void-updateunsigned-long-deltatime-0","title":"virtual void update(unsigned long deltaTime) = 0","text":"

    Updates the entity's logic. Must be implemented by derived classes.

    Parameters: - deltaTime (unsigned long): Time elapsed since the last frame in milliseconds

    Returns: - void

    Notes: - Called automatically by Scene every frame if isEnabled is true - Use deltaTime for frame-rate independent movement - Override to implement entity-specific update logic

    Example:

    void update(unsigned long deltaTime) override {\n    // Move entity\n    float speed = 50.0f;  // pixels per second\n    x += (speed * deltaTime) / 1000.0f;\n\n    // Wrap around screen\n    if (x > 128) {\n        x = 0;\n    }\n}\n

    "},{"location":"api_reference/core/entity/#virtual-void-drawrenderer-renderer-0","title":"virtual void draw(Renderer& renderer) = 0","text":"

    Renders the entity. Must be implemented by derived classes.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer to use for drawing

    Returns: - void

    Notes: - Called automatically by Scene every frame if isVisible is true - Entities are drawn in render layer order, then in add order - Override to implement entity-specific drawing logic

    Example:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Draw sprite at entity position\n    renderer.drawSprite(mySprite, static_cast<int>(x), static_cast<int>(y), Color::White);\n}\n

    "},{"location":"api_reference/core/entity/#entitytype-enum","title":"EntityType Enum","text":"

    Categorizes entities for type-safe casting and logic differentiation.

    Values: - EntityType::GENERIC: Generic entity (default) - EntityType::ACTOR: Actor entity (with collision support) - EntityType::UI_ELEMENT: UI element

    Example:

    if (entity->type == EntityType::ACTOR) {\n    Actor* actor = static_cast<Actor*>(entity);\n    // Use actor-specific methods\n}\n

    "},{"location":"api_reference/core/entity/#rect-structure","title":"Rect Structure","text":"

    Represents a 2D rectangle, typically used for hitboxes or bounds.

    Members: - float x, y: Top-left corner coordinates - int width, height: Dimensions of the rectangle

    Methods: - bool intersects(const Rect& other): Checks if this rectangle intersects with another

    Example:

    pixelroot32::core::Rect rect1{10.0f, 20.0f, 50, 50};\npixelroot32::core::Rect rect2{30.0f, 40.0f, 50, 50};\n\nif (rect1.intersects(rect2)) {\n    // Rectangles overlap\n}\n

    "},{"location":"api_reference/core/entity/#usage-example","title":"Usage Example","text":"
    #include \"core/Entity.h\"\n\nclass Collectible : public pixelroot32::core::Entity {\nprivate:\n    const pixelroot32::graphics::Sprite* sprite;\n\npublic:\n    Collectible(float x, float y) \n        : Entity(x, y, 8, 8, EntityType::GENERIC),\n          sprite(&collectibleSprite) {}\n\n    void update(unsigned long deltaTime) override {\n        // Rotate or animate\n        rotation += deltaTime * 0.001f;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        if (isVisible) {\n            renderer.drawSprite(*sprite, \n                               static_cast<int>(x), \n                               static_cast<int>(y), \n                               Color::Yellow);\n        }\n    }\n\nprivate:\n    float rotation = 0.0f;\n};\n
    "},{"location":"api_reference/core/entity/#performance-considerations","title":"Performance Considerations","text":"
    • Visibility: Use isVisible = false instead of removing entities when hiding
    • Enable state: Use isEnabled = false to pause entity logic
    • Render layers: Organize entities by layer to minimize layer switches
    • Direct access: Direct property access is fast (no function call overhead)
    "},{"location":"api_reference/core/entity/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Each entity consumes memory; stay within MAX_ENTITIES limit
    • Object pooling: Reuse entities instead of creating/destroying frequently
    • Update frequency: Disable entities that don't need to update every frame
    "},{"location":"api_reference/core/entity/#see-also","title":"See Also","text":"
    • Scene - Scene management
    • Actor - Entity with collision support
    • PhysicsActor - Entity with physics
    • Manual - Scenes and Entities
    • API Overview
    "},{"location":"api_reference/core/input_config/","title":"InputConfig","text":"

    Configuration structure for the InputManager.

    "},{"location":"api_reference/core/input_config/#description","title":"Description","text":"

    InputConfig defines the mapping between logical inputs and physical pins (ESP32) or keyboard keys (Native/SDL2). It uses variadic arguments to allow flexible configuration of any number of inputs.

    The configuration is platform-specific: ESP32 uses GPIO pin numbers, while Native uses SDL keyboard scancodes.

    "},{"location":"api_reference/core/input_config/#namespace","title":"Namespace","text":"
    namespace pixelroot32::input {\n    struct InputConfig {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/input_config/#structure","title":"Structure","text":""},{"location":"api_reference/core/input_config/#int-count","title":"int count","text":"

    Total number of configured inputs.

    Type: int

    Access: Read-write

    Default: 0

    Notes: - Must match the number of arguments provided to constructor - Determines the size of the internal button array

    "},{"location":"api_reference/core/input_config/#int-inputpins-esp32-only","title":"int* inputPins (ESP32 only)","text":"

    Array of GPIO pin numbers for ESP32.

    Type: int*

    Access: Read-write

    Default: nullptr

    Notes: - Only available on ESP32 platform - Array size equals count - Pin numbers correspond to ESP32 GPIO pins - Use nullptr if count is 0

    Example:

    // ESP32: 6 buttons on pins 0, 2, 4, 5, 18, 19\npixelroot32::input::InputConfig config(6, 0, 2, 4, 5, 18, 19);\n// config.inputPins[0] = 0  (Up)\n// config.inputPins[1] = 2  (Down)\n// config.inputPins[2] = 4  (Left)\n// config.inputPins[3] = 5  (Right)\n// config.inputPins[4] = 18 (Button A)\n// config.inputPins[5] = 19 (Button B)\n

    "},{"location":"api_reference/core/input_config/#uint8_t-buttonnames-native-only","title":"uint8_t* buttonNames (Native only)","text":"

    Array of button mappings (scancodes) for Native.

    Type: uint8_t*

    Access: Read-write

    Default: nullptr

    Notes: - Only available on Native platform - Array size equals count - Values are SDL keyboard scancodes - Use nullptr if count is 0

    Example:

    // Native: Map to keyboard keys\n#include <SDL2/SDL.h>\n\npixelroot32::input::InputConfig config(6,\n    SDL_SCANCODE_UP,    // Index 0\n    SDL_SCANCODE_DOWN,  // Index 1\n    SDL_SCANCODE_LEFT,  // Index 2\n    SDL_SCANCODE_RIGHT, // Index 3\n    SDL_SCANCODE_X,     // Index 4 (Button A)\n    SDL_SCANCODE_Z      // Index 5 (Button B)\n);\n

    "},{"location":"api_reference/core/input_config/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/input_config/#inputconfigint-count","title":"InputConfig(int count, ...)","text":"

    Constructs a new InputConfig with variadic arguments.

    Parameters: - count (int): Number of inputs to configure - ... (variadic): Variable arguments list of pins (ESP32) or scancodes (Native)

    Notes: - If count <= 0, configuration is empty (nullptr arrays) - Allocates arrays dynamically based on count - Arguments must match count in number - Platform-specific: ESP32 expects int (GPIO pins), Native expects int (SDL scancodes)

    ESP32 Example:

    // Configure 4 directional buttons\npixelroot32::input::InputConfig config(4, 0, 2, 4, 5);\n// Pin 0 = Up, Pin 2 = Down, Pin 4 = Left, Pin 5 = Right\n\n// Configure 6 buttons (4 directions + 2 action buttons)\npixelroot32::input::InputConfig config(6, 0, 2, 4, 5, 18, 19);\n

    Native Example:

    #include <SDL2/SDL.h>\n\n// Configure 4 directional buttons\npixelroot32::input::InputConfig config(4,\n    SDL_SCANCODE_UP,\n    SDL_SCANCODE_DOWN,\n    SDL_SCANCODE_LEFT,\n    SDL_SCANCODE_RIGHT\n);\n\n// Configure 6 buttons (4 directions + 2 action buttons)\npixelroot32::input::InputConfig config(6,\n    SDL_SCANCODE_UP,\n    SDL_SCANCODE_DOWN,\n    SDL_SCANCODE_LEFT,\n    SDL_SCANCODE_RIGHT,\n    SDL_SCANCODE_X,  // Button A\n    SDL_SCANCODE_Z   // Button B\n);\n

    "},{"location":"api_reference/core/input_config/#usage-example","title":"Usage Example","text":""},{"location":"api_reference/core/input_config/#esp32-configuration","title":"ESP32 Configuration","text":"
    #include \"input/InputConfig.h\"\n#include \"input/InputManager.h\"\n#include \"core/Engine.h\"\n\nvoid setup() {\n    // Configure input: 6 buttons\n    // Pins: Up=0, Down=2, Left=4, Right=5, A=18, B=19\n    pixelroot32::input::InputConfig inputConfig(6, 0, 2, 4, 5, 18, 19);\n\n    // Create input manager\n    pixelroot32::input::InputManager inputManager(inputConfig);\n    inputManager.init();\n\n    // Or use with Engine\n    pixelroot32::graphics::DisplayConfig displayConfig;\n    pixelroot32::core::Engine engine(displayConfig, inputConfig);\n    engine.init();\n    engine.run();\n}\n
    "},{"location":"api_reference/core/input_config/#native-configuration","title":"Native Configuration","text":"
    #include \"input/InputConfig.h\"\n#include <SDL2/SDL.h>\n\nvoid setup() {\n    // Configure input: 6 buttons mapped to keyboard\n    pixelroot32::input::InputConfig inputConfig(6,\n        SDL_SCANCODE_UP,    // Index 0: Up\n        SDL_SCANCODE_DOWN,  // Index 1: Down\n        SDL_SCANCODE_LEFT,  // Index 2: Left\n        SDL_SCANCODE_RIGHT, // Index 3: Right\n        SDL_SCANCODE_X,     // Index 4: Button A\n        SDL_SCANCODE_Z      // Index 5: Button B\n    );\n\n    // Use with Engine\n    pixelroot32::graphics::DisplayConfig displayConfig;\n    pixelroot32::core::Engine engine(displayConfig, inputConfig);\n    engine.init();\n    engine.run();\n}\n
    "},{"location":"api_reference/core/input_config/#platform-agnostic-configuration","title":"Platform-Agnostic Configuration","text":"
    #ifdef PLATFORM_ESP32\n    // ESP32: Use GPIO pins\n    pixelroot32::input::InputConfig inputConfig(6, 0, 2, 4, 5, 18, 19);\n#elif PLATFORM_NATIVE\n    // Native: Use SDL scancodes\n    #include <SDL2/SDL.h>\n    pixelroot32::input::InputConfig inputConfig(6,\n        SDL_SCANCODE_UP,\n        SDL_SCANCODE_DOWN,\n        SDL_SCANCODE_LEFT,\n        SDL_SCANCODE_RIGHT,\n        SDL_SCANCODE_X,\n        SDL_SCANCODE_Z\n    );\n#endif\n
    "},{"location":"api_reference/core/input_config/#button-index-mapping","title":"Button Index Mapping","text":"

    Button indices are determined by the order in the constructor:

    Typical Convention: - Index 0: Up / Primary action - Index 1: Down / Secondary action - Index 2: Left - Index 3: Right - Index 4+: Additional buttons

    Example:

    // 4-button D-pad\nInputConfig config(4, UP_PIN, DOWN_PIN, LEFT_PIN, RIGHT_PIN);\n// Index 0 = Up, Index 1 = Down, Index 2 = Left, Index 3 = Right\n\n// 6-button setup (D-pad + 2 action buttons)\nInputConfig config(6, UP_PIN, DOWN_PIN, LEFT_PIN, RIGHT_PIN, A_PIN, B_PIN);\n// Index 0-3 = D-pad, Index 4 = A, Index 5 = B\n

    "},{"location":"api_reference/core/input_config/#esp32-pin-considerations","title":"ESP32 Pin Considerations","text":"
    • GPIO pins: Use any available GPIO pin
    • Pull-up/pull-down: Configure resistors appropriately
    • Input mode: Pins are automatically configured as inputs
    • Restrictions: Some pins have special functions (check ESP32 datasheet)

    Common Pin Choices: - GPIO 0, 2, 4, 5: Safe for buttons (watch for boot mode pins) - GPIO 18, 19: Good for additional buttons - Avoid: GPIO 6-11 (flash), GPIO 34-39 (input only, no pull-up)

    "},{"location":"api_reference/core/input_config/#native-sdl-scancode-reference","title":"Native SDL Scancode Reference","text":"

    Common SDL scancodes:

    • SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT: Arrow keys
    • SDL_SCANCODE_W, SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_D: WASD
    • SDL_SCANCODE_X, SDL_SCANCODE_Z: Common action buttons
    • SDL_SCANCODE_SPACE: Spacebar
    • SDL_SCANCODE_RETURN: Enter key

    Example:

    // WASD + Space + Enter\npixelroot32::input::InputConfig config(6,\n    SDL_SCANCODE_W,        // Up\n    SDL_SCANCODE_S,        // Down\n    SDL_SCANCODE_A,        // Left\n    SDL_SCANCODE_D,         // Right\n    SDL_SCANCODE_SPACE,    // Jump\n    SDL_SCANCODE_RETURN    // Action\n);\n

    "},{"location":"api_reference/core/input_config/#performance-considerations","title":"Performance Considerations","text":"
    • Memory: Arrays are allocated dynamically (small overhead)
    • Configuration: Done once at startup, no runtime cost
    • Access: Button indices are fast (array access)
    "},{"location":"api_reference/core/input_config/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Pin configuration: Ensure pins are not used by other peripherals
    • Debouncing: Hardware debouncing recommended for reliable input
    • Power: Buttons should use pull-up resistors to avoid floating pins
    "},{"location":"api_reference/core/input_config/#see-also","title":"See Also","text":"
    • InputManager - Input handling
    • Engine - Engine that uses InputConfig
    • Manual - Input and Control
    • API Overview
    "},{"location":"api_reference/core/input_manager/","title":"InputManager","text":"

    Handles input from physical buttons or keyboard (on PC).

    "},{"location":"api_reference/core/input_manager/#description","title":"Description","text":"

    The InputManager polls configured pins (ESP32) or keyboard state (Native), handles debouncing, and tracks button states (Pressed, Released, Down, Clicked). It provides a unified input interface for both platforms.

    The manager supports edge detection (just pressed/released) and continuous state (held down), making it suitable for both gameplay and UI navigation.

    "},{"location":"api_reference/core/input_manager/#namespace","title":"Namespace","text":"
    namespace pixelroot32::input {\n    class InputManager {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/input_manager/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Engine (manages input manager instance)
    "},{"location":"api_reference/core/input_manager/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/input_manager/#inputmanagerconst-inputconfig-config","title":"InputManager(const InputConfig& config)","text":"

    Constructs the InputManager with a specific configuration.

    Parameters: - config (const InputConfig&): The input configuration (pins, button count)

    Example:

    #include \"input/InputManager.h\"\n#include \"input/InputConfig.h\"\n\n// ESP32: Configure GPIO pins\npixelroot32::input::InputConfig inputConfig(6, 0, 2, 4, 5, 18, 19);\npixelroot32::input::InputManager inputManager(inputConfig);\ninputManager.init();\n\n// Native: Configure keyboard keys\n// (Configuration handled differently on Native)\n

    "},{"location":"api_reference/core/input_manager/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/input_manager/#void-init","title":"void init()","text":"

    Initializes the input pins.

    Returns: - void

    Notes: - Must be called after construction and before use - Configures GPIO pins (ESP32) or keyboard state (Native) - Safe to call multiple times (idempotent) - Typically called automatically by Engine::init()

    Example:

    InputManager inputManager(inputConfig);\ninputManager.init();  // Initialize before use\n

    "},{"location":"api_reference/core/input_manager/#void-updateunsigned-long-dt","title":"void update(unsigned long dt)","text":"

    Updates input state by polling hardware pins (ESP32) or keyboard state (Native).

    Parameters: - dt (unsigned long): Delta time in milliseconds

    Returns: - void

    Notes: - Must be called every frame for proper input detection - Handles debouncing automatically - Updates button states and edge detection - Typically called automatically by Engine::update()

    ESP32 Example:

    void update(unsigned long deltaTime) override {\n    // Input is updated automatically by Engine\n    // Access input via engine.getInputManager()\n}\n

    Native Example:

    // On Native, update is called with keyboard state:\nvoid update(unsigned long dt, const uint8_t* keyboardState);\n

    "},{"location":"api_reference/core/input_manager/#bool-isbuttonpresseduint8_t-buttonindex-const","title":"bool isButtonPressed(uint8_t buttonIndex) const","text":"

    Checks if a button was just pressed this frame.

    Parameters: - buttonIndex (uint8_t): Index of the button to check (0-based)

    Returns: - bool: true if the button transitioned from UP to DOWN this frame

    Notes: - Returns true only on the frame the button was pressed - Useful for one-time actions (jump, shoot, menu select) - Resets automatically on next frame

    Example:

    auto& input = engine.getInputManager();\n\nif (input.isButtonPressed(0)) {  // Button A (index 0)\n    // Jump (only once per press)\n    player->jump();\n}\n\nif (input.isButtonPressed(1)) {  // Button B (index 1)\n    // Shoot (only once per press)\n    player->shoot();\n}\n

    "},{"location":"api_reference/core/input_manager/#bool-isbuttonreleaseduint8_t-buttonindex-const","title":"bool isButtonReleased(uint8_t buttonIndex) const","text":"

    Checks if a button was just released this frame.

    Parameters: - buttonIndex (uint8_t): Index of the button to check

    Returns: - bool: true if the button transitioned from DOWN to UP this frame

    Notes: - Returns true only on the frame the button was released - Useful for detecting button release events - Less commonly used than isButtonPressed()

    Example:

    auto& input = engine.getInputManager();\n\nif (input.isButtonReleased(0)) {\n    // Button A was just released\n    player->stopCharging();\n}\n

    "},{"location":"api_reference/core/input_manager/#bool-isbuttonclickeduint8_t-buttonindex-const","title":"bool isButtonClicked(uint8_t buttonIndex) const","text":"

    Checks if a button was clicked (pressed and released).

    Parameters: - buttonIndex (uint8_t): Index of the button to check

    Returns: - bool: true if the button was clicked (pressed then released)

    Notes: - Returns true when button is released after being pressed - Useful for UI buttons and menu selection - Detects complete press-release cycle

    Example:

    auto& input = engine.getInputManager();\n\nif (input.isButtonClicked(0)) {  // Button A clicked\n    // Select menu item\n    menu->select();\n}\n

    "},{"location":"api_reference/core/input_manager/#bool-isbuttondownuint8_t-buttonindex-const","title":"bool isButtonDown(uint8_t buttonIndex) const","text":"

    Checks if a button is currently held down.

    Parameters: - buttonIndex (uint8_t): Index of the button to check

    Returns: - bool: true if the button is currently in the DOWN state

    Notes: - Returns true for as long as the button is held - Useful for continuous actions (movement, charging) - Use with deltaTime for frame-rate independent movement

    Example:

    auto& input = engine.getInputManager();\n\nfloat speed = 100.0f;  // pixels per second\nfloat vx = 0.0f, vy = 0.0f;\n\nif (input.isButtonDown(2)) {  // Left button\n    vx = -speed;\n}\nif (input.isButtonDown(3)) {  // Right button\n    vx = speed;\n}\nif (input.isButtonDown(0)) {  // Up button\n    vy = -speed;\n}\nif (input.isButtonDown(1)) {  // Down button\n    vy = speed;\n}\n\n// Apply movement (frame-rate independent)\nx += (vx * deltaTime) / 1000.0f;\ny += (vy * deltaTime) / 1000.0f;\n

    "},{"location":"api_reference/core/input_manager/#button-indices","title":"Button Indices","text":"

    Button indices are defined by the order in InputConfig:

    Typical Mapping: - 0: Up / Button A - 1: Down / Button B - 2: Left - 3: Right - 4: Additional button 1 - 5: Additional button 2

    Example:

    // Configure 6 buttons: Up, Down, Left, Right, A, B\npixelroot32::input::InputConfig inputConfig(6, \n    GPIO_UP,    // Index 0\n    GPIO_DOWN,  // Index 1\n    GPIO_LEFT,  // Index 2\n    GPIO_RIGHT, // Index 3\n    GPIO_A,     // Index 4\n    GPIO_B      // Index 5\n);\n\n// Use indices\nif (input.isButtonDown(2)) {  // Left\n    moveLeft();\n}\nif (input.isButtonPressed(4)) {  // A button\n    jump();\n}\n

    "},{"location":"api_reference/core/input_manager/#usage-example","title":"Usage Example","text":"
    #include \"input/InputManager.h\"\n#include \"core/Engine.h\"\n\nclass PlayerController {\nprivate:\n    pixelroot32::core::Engine& engine;\n\npublic:\n    PlayerController(pixelroot32::core::Engine& eng) : engine(eng) {}\n\n    void update(unsigned long deltaTime) {\n        auto& input = engine.getInputManager();\n\n        // Movement (continuous)\n        float speed = 150.0f;  // pixels per second\n        float vx = 0.0f, vy = 0.0f;\n\n        if (input.isButtonDown(3)) {  // Right\n            vx = speed;\n        }\n        if (input.isButtonDown(2)) {  // Left\n            vx = -speed;\n        }\n        if (input.isButtonDown(0)) {  // Up\n            vy = -speed;\n        }\n        if (input.isButtonDown(1)) {  // Down\n            vy = speed;\n        }\n\n        // Apply movement\n        playerX += (vx * deltaTime) / 1000.0f;\n        playerY += (vy * deltaTime) / 1000.0f;\n\n        // Actions (one-time)\n        if (input.isButtonPressed(4)) {  // A button\n            player->jump();\n        }\n\n        if (input.isButtonPressed(5)) {  // B button\n            player->shoot();\n        }\n    }\n};\n
    "},{"location":"api_reference/core/input_manager/#input-state-comparison","title":"Input State Comparison","text":"Method Returns true when Use Case isButtonPressed() Button just pressed this frame One-time actions (jump, shoot) isButtonReleased() Button just released this frame Release events (stop charging) isButtonClicked() Button pressed then released UI buttons, menu selection isButtonDown() Button currently held Continuous actions (movement)"},{"location":"api_reference/core/input_manager/#performance-considerations","title":"Performance Considerations","text":"
    • Update frequency: update() must be called every frame
    • Debouncing: Handled automatically, no performance impact
    • State queries: All query methods are fast (inline accessors)
    • Memory: Button state arrays are small and efficient
    "},{"location":"api_reference/core/input_manager/#esp32-considerations","title":"ESP32 Considerations","text":"
    • GPIO pins: Configure pins in InputConfig
    • Pull-up/pull-down: Ensure proper resistor configuration
    • Debouncing: Hardware debouncing recommended for noisy buttons
    • Pin limits: Some ESP32 pins have restrictions (check datasheet)
    "},{"location":"api_reference/core/input_manager/#native-considerations","title":"Native Considerations","text":"
    • Keyboard mapping: Uses SDL scancodes
    • Key detection: Automatically handles keyboard state
    • Multiple keys: Can detect multiple keys simultaneously
    "},{"location":"api_reference/core/input_manager/#see-also","title":"See Also","text":"
    • InputConfig - Input configuration
    • Engine - Engine that manages InputManager
    • Manual - Input and Control
    • API Overview
    "},{"location":"api_reference/core/physics_actor/","title":"PhysicsActor","text":"

    An actor with basic 2D physics properties.

    "},{"location":"api_reference/core/physics_actor/#description","title":"Description","text":"

    PhysicsActor extends the base Actor class by adding velocity, acceleration, friction, restitution (bounciness), and world boundary collision resolution. It is designed for objects that need to move and bounce within a defined area, such as balls, projectiles, or platformer characters.

    PhysicsActor automatically handles: - Velocity-based movement - Friction application - World boundary collision and bouncing - Collision callbacks

    "},{"location":"api_reference/core/physics_actor/#namespace","title":"Namespace","text":"
    namespace pixelroot32::core {\n    class PhysicsActor : public Actor {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/physics_actor/#inheritance","title":"Inheritance","text":"
    • Inherits from: Actor
    • Inherited by: Your custom physics-enabled actor classes
    "},{"location":"api_reference/core/physics_actor/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/physics_actor/#physicsactorfloat-x-float-y-float-w-float-h","title":"PhysicsActor(float x, float y, float w, float h)","text":"

    Creates a physics-enabled actor with specified position and size.

    Parameters: - x (float): Initial X position in world space - y (float): Initial Y position in world space - w (float): Actor width in pixels - h (float): Actor height in pixels

    Notes: - Velocity starts at (0, 0) - Restitution defaults to 1.0 (perfect bounce) - Friction defaults to 0.0 (no friction) - No world limits by default

    Example:

    class BallActor : public pixelroot32::core::PhysicsActor {\npublic:\n    BallActor(float x, float y) \n        : PhysicsActor(x, y, 8.0f, 8.0f) {\n        // Set physics properties\n        setRestitution(0.8f);  // 80% bounce\n        setFriction(0.1f);     // Small friction\n        setWorldSize(128, 128); // World bounds\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledCircle(static_cast<int>(x + width/2), \n                                 static_cast<int>(y + height/2), \n                                 width/2, \n                                 Color::White);\n    }\n\n    Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(Actor* other) override {\n        // Bounce off other actors\n    }\n\n    void onWorldCollision() override {\n        // Play bounce sound\n    }\n};\n

    "},{"location":"api_reference/core/physics_actor/#protected-properties","title":"Protected Properties","text":""},{"location":"api_reference/core/physics_actor/#float-vx-vy","title":"float vx, vy","text":"

    Horizontal and vertical velocity components.

    Type: float

    Access: Protected (use setVelocity() to modify)

    Default: 0.0f

    Notes: - Velocity is in pixels per second - Automatically applied during update() - Modified by friction and world collisions

    "},{"location":"api_reference/core/physics_actor/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/physics_actor/#void-setvelocityfloat-x-float-y","title":"void setVelocity(float x, float y)","text":"

    Sets the linear velocity of the actor.

    Parameters: - x (float): Horizontal velocity in pixels per second - y (float): Vertical velocity in pixels per second

    Returns: - void

    Notes: - Velocity is applied every frame during update() - Use for initial velocity or impulse-based movement - Can be called every frame for continuous control

    Example:

    // Set initial velocity\nphysicsActor->setVelocity(100.0f, -200.0f);  // Move right and up\n\n// Continuous control (e.g., player movement)\nvoid update(unsigned long deltaTime) override {\n    PhysicsActor::update(deltaTime);\n\n    float speed = 150.0f;\n    float vx = 0.0f, vy = 0.0f;\n\n    if (input.isButtonDown(Buttons::LEFT)) vx = -speed;\n    if (input.isButtonDown(Buttons::RIGHT)) vx = speed;\n    if (input.isButtonDown(Buttons::UP)) vy = -speed;\n    if (input.isButtonDown(Buttons::DOWN)) vy = speed;\n\n    setVelocity(vx, vy);\n}\n

    "},{"location":"api_reference/core/physics_actor/#void-setrestitutionfloat-r","title":"void setRestitution(float r)","text":"

    Sets the restitution (bounciness) of the actor.

    Parameters: - r (float): Restitution value (0.0 to 1.0+) - 0.0: No bounce (stops on impact) - 1.0: Perfect bounce (no energy loss) - > 1.0: Energy gain (unrealistic but possible)

    Returns: - void

    Notes: - Applied when actor collides with world boundaries - Higher values = more bouncy - Typical values: 0.5-0.9 for realistic bouncing

    Example:

    ball->setRestitution(0.8f);  // 80% bounce\n

    "},{"location":"api_reference/core/physics_actor/#void-setfrictionfloat-f","title":"void setFriction(float f)","text":"

    Sets the friction coefficient.

    Parameters: - f (float): Friction value - 0.0: No friction (object continues moving) - > 0.0: Friction applied to velocity each frame

    Returns: - void

    Notes: - Applied every frame to reduce velocity - Higher values = more friction (slower movement) - Typical values: 0.05-0.2 for smooth deceleration

    Example:

    player->setFriction(0.1f);  // Light friction\n

    "},{"location":"api_reference/core/physics_actor/#void-setlimitslimitrect-limits","title":"void setLimits(LimitRect limits)","text":"

    Sets custom movement limits for the actor.

    Parameters: - limits (LimitRect): A rectangle defining the allowed area

    Returns: - void

    Notes: - Overrides world size limits - Use -1 for any boundary to disable that limit - Actor will bounce off these boundaries

    Example:

    pixelroot32::core::LimitRect limits;\nlimits.left = 0;\nlimits.top = 0;\nlimits.right = 128;\nlimits.bottom = 128;\nphysicsActor->setLimits(limits);\n

    "},{"location":"api_reference/core/physics_actor/#void-setworldsizeint-width-int-height","title":"void setWorldSize(int width, int height)","text":"

    Defines the world size for boundary checking.

    Parameters: - width (int): Width of the world in pixels - height (int): Height of the world in pixels

    Returns: - void

    Notes: - Used as default limits if no custom LimitRect is provided - Actor will bounce off world boundaries - Set to display size for screen boundaries

    Example:

    physicsActor->setWorldSize(128, 128);  // Match display size\n

    "},{"location":"api_reference/core/physics_actor/#worldcollisioninfo-getworldcollisioninfo-const","title":"WorldCollisionInfo getWorldCollisionInfo() const","text":"

    Gets information about collisions with the world boundaries.

    Returns: - WorldCollisionInfo: A struct containing collision flags (left, right, top, bottom)

    Notes: - Updated every frame during update() - Use to detect which boundary was hit - Useful for sound effects or special behaviors

    Example:

    void update(unsigned long deltaTime) override {\n    PhysicsActor::update(deltaTime);\n\n    auto collision = getWorldCollisionInfo();\n    if (collision.left || collision.right) {\n        // Hit side wall\n        playSound(wallHitSound);\n    }\n    if (collision.top || collision.bottom) {\n        // Hit top or bottom\n        playSound(ceilingHitSound);\n    }\n}\n

    "},{"location":"api_reference/core/physics_actor/#virtual-void-oncollisionactor-other-override","title":"virtual void onCollision(Actor* other) override","text":"

    Callback triggered when this actor collides with another actor.

    Parameters: - other (Actor*): Pointer to the actor involved in the collision

    Returns: - void

    Notes: - Called automatically by the collision system - Override to implement custom collision responses - Default implementation does nothing

    Example:

    void onCollision(Actor* other) override {\n    if (other->isInLayer(DefaultLayers::kEnemy)) {\n        // Bounce off enemy\n        vx = -vx * 0.5f;\n        vy = -vy * 0.5f;\n    }\n}\n

    "},{"location":"api_reference/core/physics_actor/#virtual-void-onworldcollision","title":"virtual void onWorldCollision()","text":"

    Callback triggered when this actor collides with world boundaries.

    Returns: - void

    Notes: - Called automatically when a world boundary collision occurs - Override to implement custom behavior (sound effects, particles, etc.) - Default implementation does nothing

    Example:

    void onWorldCollision() override {\n    // Play bounce sound\n    auto& audio = engine.getAudioEngine();\n    pixelroot32::audio::AudioEvent sound{};\n    sound.type = pixelroot32::audio::WaveType::NOISE;\n    sound.frequency = 500.0f;\n    sound.duration = 0.05f;\n    audio.playEvent(sound);\n\n    // Spawn particles\n    spawnBounceParticles();\n}\n

    "},{"location":"api_reference/core/physics_actor/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the actor state. Applies physics integration and checks for world boundary collisions.

    Parameters: - deltaTime (unsigned long): Time elapsed since the last frame in milliseconds

    Returns: - void

    Notes: - Called automatically by Scene if isEnabled is true - Applies velocity to position - Applies friction to velocity - Resolves world boundary collisions - Override to add custom update logic, but call PhysicsActor::update(deltaTime) first

    Example:

    void update(unsigned long deltaTime) override {\n    // Apply physics\n    PhysicsActor::update(deltaTime);\n\n    // Custom logic\n    if (shouldApplyGravity) {\n        vy += gravity * (deltaTime / 1000.0f);\n    }\n}\n

    "},{"location":"api_reference/core/physics_actor/#limitrect-structure","title":"LimitRect Structure","text":"

    Bounding rectangle for world-collision resolution.

    Members: - int left: Left boundary (-1 means no limit) - int top: Top boundary (-1 means no limit) - int right: Right boundary (-1 means no limit) - int bottom: Bottom boundary (-1 means no limit)

    Methods: - int width() const: Calculates width (right - left) - int height() const: Calculates height (bottom - top)

    Example:

    pixelroot32::core::LimitRect limits(10, 10, 118, 118);  // 10px margin\nphysicsActor->setLimits(limits);\n

    "},{"location":"api_reference/core/physics_actor/#worldcollisioninfo-structure","title":"WorldCollisionInfo Structure","text":"

    Information about world collisions in the current frame.

    Members: - bool left: True if collided with the left boundary - bool right: True if collided with the right boundary - bool top: True if collided with the top boundary - bool bottom: True if collided with the bottom boundary

    Example:

    auto collision = physicsActor->getWorldCollisionInfo();\nif (collision.bottom) {\n    // On ground\n    canJump = true;\n}\n

    "},{"location":"api_reference/core/physics_actor/#usage-example","title":"Usage Example","text":"
    #include \"core/PhysicsActor.h\"\n\nclass BouncingBall : public pixelroot32::core::PhysicsActor {\npublic:\n    BouncingBall(float x, float y) \n        : PhysicsActor(x, y, 8.0f, 8.0f) {\n        // Set physics properties\n        setRestitution(0.9f);  // Very bouncy\n        setFriction(0.05f);    // Light friction\n        setWorldSize(128, 128);\n\n        // Set initial velocity\n        setVelocity(100.0f, -150.0f);\n\n        // Set collision layer\n        layer = pixelroot32::physics::DefaultLayers::kProjectile;\n        mask = pixelroot32::physics::DefaultLayers::kObstacle;\n    }\n\n    void update(unsigned long deltaTime) override {\n        PhysicsActor::update(deltaTime);\n\n        // Apply gravity\n        vy += 200.0f * (deltaTime / 1000.0f);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledCircle(static_cast<int>(x + width/2), \n                                 static_cast<int>(y + height/2), \n                                 width/2, \n                                 Color::White);\n    }\n\n    Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(Actor* other) override {\n        // Bounce off obstacles\n        vx = -vx * 0.8f;\n        vy = -vy * 0.8f;\n    }\n\n    void onWorldCollision() override {\n        // Play bounce sound\n        playBounceSound();\n    }\n};\n
    "},{"location":"api_reference/core/physics_actor/#performance-considerations","title":"Performance Considerations","text":"
    • Physics integration: Very efficient (simple velocity integration)
    • World bounds: Boundary checks are fast (AABB)
    • Friction: Applied every frame; keep friction values reasonable
    • Collision callbacks: Keep onCollision() and onWorldCollision() fast
    "},{"location":"api_reference/core/physics_actor/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Floating point: Uses float math; acceptable for ESP32 but integer math would be faster
    • Frame rate: Physics is frame-rate independent (uses deltaTime)
    • Memory: Each PhysicsActor consumes more memory than Actor (velocity, limits, etc.)
    "},{"location":"api_reference/core/physics_actor/#see-also","title":"See Also","text":"
    • Actor - Base actor class
    • CollisionSystem - Collision detection
    • Manual - Physics and Collisions
    • API Overview
    "},{"location":"api_reference/core/scene/","title":"Scene","text":"

    Represents a game level or screen containing entities.

    "},{"location":"api_reference/core/scene/#description","title":"Description","text":"

    A Scene manages a collection of Entities and a CollisionSystem. It is responsible for updating and drawing all entities it contains. Scenes provide lifecycle hooks (init(), update(), draw()) to manage gameplay segments.

    Scenes are the primary organizational unit in PixelRoot32, similar to levels or screens in other game engines. Each scene can contain up to MAX_ENTITIES (default 32; overridable via compiler flags) entities, and drawing uses up to MAX_LAYERS (default 3; overridable) render layers.

    "},{"location":"api_reference/core/scene/#namespace","title":"Namespace","text":"
    namespace pixelroot32::core {\n    class Scene {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/scene/#inheritance","title":"Inheritance","text":"
    • Base class: None (abstract base class)
    • Inherited by: Your custom scene classes (e.g., MainMenuScene, GameScene)
    "},{"location":"api_reference/core/scene/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/scene/#scene_1","title":"Scene()","text":"

    Creates an empty scene ready to be populated with entities.

    Notes: - The scene starts with no entities - init() should be called when the scene becomes active - The collision system is automatically initialized

    Example:

    class MyScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Initialize scene resources\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);  // Update entities and collisions\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        Scene::draw(renderer);  // Draw all entities\n    }\n};\n

    "},{"location":"api_reference/core/scene/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/scene/#virtual-void-init","title":"virtual void init()","text":"

    Initializes the scene. Called when entering the scene.

    Returns: - void

    Notes: - Called automatically when the scene is set via Engine::setScene() - Override this method to initialize scene-specific resources - Safe to call multiple times (idempotent) - Add entities here or in the constructor

    Example:

    class GameScene : public pixelroot32::core::Scene {\nprivate:\n    PlayerActor* player;\n    std::array<EnemyActor*, 10> enemies;\n\npublic:\n    void init() override {\n        // Create player\n        player = new PlayerActor();\n        addEntity(player);\n\n        // Create enemies\n        for (int i = 0; i < 10; i++) {\n            enemies[i] = new EnemyActor();\n            addEntity(enemies[i]);\n        }\n    }\n};\n

    "},{"location":"api_reference/core/scene/#virtual-void-updateunsigned-long-deltatime","title":"virtual void update(unsigned long deltaTime)","text":"

    Updates all entities in the scene and handles collisions.

    Parameters: - deltaTime (unsigned long): Time elapsed since last frame in milliseconds

    Notes: - Called automatically by the engine every frame - Updates all entities in the scene - Processes collisions between actors - Override to add custom update logic, but call Scene::update(deltaTime) to maintain entity updates

    Example:

    void update(unsigned long deltaTime) override {\n    // Custom update logic\n    gameTimer += deltaTime;\n\n    // Update entities and collisions\n    Scene::update(deltaTime);\n\n    // Additional logic after entity updates\n    if (gameTimer > 60000) {\n        // Game over after 60 seconds\n    }\n}\n

    "},{"location":"api_reference/core/scene/#virtual-void-drawrenderer-renderer","title":"virtual void draw(Renderer& renderer)","text":"

    Draws all visible entities in the scene.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): The renderer to use for drawing

    Notes: - Called automatically by the engine every frame after update() - Draws all visible entities in the scene - Override to add custom drawing logic, but call Scene::draw(renderer) to maintain entity rendering - Entities are drawn in the order they were added

    Example:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Draw background\n    renderer.drawTileMap(backgroundTileMap, 0, 0, Color::White);\n\n    // Draw all entities\n    Scene::draw(renderer);\n\n    // Draw UI overlay\n    renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n}\n

    "},{"location":"api_reference/core/scene/#void-addentityentity-entity","title":"void addEntity(Entity* entity)","text":"

    Adds an entity to the scene.

    Parameters: - entity (Entity*): Pointer to the Entity to add. Must not be nullptr.

    Notes: - Entities are added to an internal queue - Maximum of MAX_ENTITIES (default 32; overridable) entities per scene - If the limit is reached, the entity may not be added (check return value if available) - Entities are updated and drawn in the order they were added - The entity's lifetime is managed by the scene (do not delete manually while in scene)

    Example:

    void init() override {\n    // Create and add player\n    PlayerActor* player = new PlayerActor();\n    player->setPosition(64, 64);\n    addEntity(player);\n\n    // Create and add enemy\n    EnemyActor* enemy = new EnemyActor();\n    enemy->setPosition(100, 100);\n    addEntity(enemy);\n}\n

    "},{"location":"api_reference/core/scene/#void-removeentityentity-entity","title":"void removeEntity(Entity* entity)","text":"

    Removes an entity from the scene.

    Parameters: - entity (Entity*): Pointer to the Entity to remove

    Notes: - The entity is removed from the update and draw queues - The entity is not deleted automatically (you must manage its lifetime) - Safe to call even if the entity is not in the scene - Consider using object pooling instead of frequent add/remove

    Example:

    void onEnemyDestroyed(EnemyActor* enemy) {\n    removeEntity(enemy);\n    // Return to pool or delete\n    enemyPool.returnToPool(enemy);\n}\n

    "},{"location":"api_reference/core/scene/#void-clearentities","title":"void clearEntities()","text":"

    Removes all entities from the scene.

    Notes: - All entities are removed from the update and draw queues - Entities are not deleted automatically (you must manage their lifetimes) - Useful for scene cleanup or reset - Consider using object pooling to reuse entities

    Example:

    void reset() {\n    clearEntities();\n    // Return all entities to pool\n    for (auto* entity : entityPool) {\n        entityPool.returnToPool(entity);\n    }\n}\n

    "},{"location":"api_reference/core/scene/#protected-members","title":"Protected Members","text":""},{"location":"api_reference/core/scene/#arduinoqueue-entities","title":"ArduinoQueue entities

    Queue of entities in the scene. Accessible to derived classes for custom entity management.

    Type: ArduinoQueue<Entity*>

    Notes: - Maximum capacity: MAX_ENTITIES (default 32; overridable) - Direct access allows custom iteration or filtering - Use with caution: modifying while iterating may cause issues

    ","text":""},{"location":"api_reference/core/scene/#collisionsystem-collisionsystem","title":"CollisionSystem collisionSystem

    System to handle collisions between actors. Accessible to derived classes for custom collision handling.

    Type: pixelroot32::physics::CollisionSystem

    Notes: - Automatically processes collisions between actors - Uses collision layers and masks for filtering - Can be accessed for custom collision queries

    ","text":""},{"location":"api_reference/core/scene/#overriding-scene-limits-max_layers-max_entities","title":"Overriding scene limits (MAX_LAYERS / MAX_ENTITIES)","text":"

    The engine defines default limits in core/Scene.h: MAX_LAYERS (default 3) and MAX_ENTITIES (default 32). These are guarded with #ifndef, so you can override them from your project without modifying the engine.

    ESP32 platform limitation

    The default of 3 for MAX_LAYERS is due to ESP32 platform constraints (memory and draw-loop cost). On native/PC you can safely use a higher value; on ESP32, increasing it may affect performance or memory.

    "},{"location":"api_reference/core/scene/#option-a-compiler-flags-recommended","title":"Option A: Compiler flags (recommended)

    In your project (e.g. in platformio.ini), add the defines to build_flags for the environment you use:

    build_flags =\n    -DMAX_LAYERS=5\n    -DMAX_ENTITIES=64\n

    The compiler defines MAX_LAYERS and MAX_ENTITIES before processing any .cpp file. Because Scene.h uses #ifndef MAX_LAYERS / #ifndef MAX_ENTITIES, it will not redefine them and your values will be used.

    Effect: - MAX_LAYERS: Number of render layers drawn in Scene::draw() (layer 0 = background, 1+ = sprite context). Increasing this allows more distinct draw layers (e.g. background, platforms, gameplay, foreground, UI). - MAX_ENTITIES: On Arduino, the capacity of the scene entity queue when constructed with this value. On native (mock queue), the value is ignored (unbounded).

    See also: Platforms and Drivers - Scene limits.

    ","text":""},{"location":"api_reference/core/scene/#usage-example","title":"Usage Example","text":"
    #include \"core/Scene.h\"\n#include \"core/Actor.h\"\n\nclass MyGameScene : public pixelroot32::core::Scene {\nprivate:\n    PlayerActor* player;\n\npublic:\n    void init() override {\n        // Create player\n        player = new PlayerActor();\n        player->setPosition(64, 64);\n        addEntity(player);\n\n        // Create some enemies\n        for (int i = 0; i < 5; i++) {\n            EnemyActor* enemy = new EnemyActor();\n            enemy->setPosition(10 + i * 20, 10);\n            addEntity(enemy);\n        }\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Custom game logic\n        if (player->isDead()) {\n            // Handle game over\n        }\n\n        // Update entities and collisions\n        Scene::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw background\n        renderer.drawFilledRectangle(0, 0, 128, 128, Color::Black);\n\n        // Draw all entities\n        Scene::draw(renderer);\n\n        // Draw HUD\n        char scoreText[32];\n        snprintf(scoreText, sizeof(scoreText), \"Score: %d\", score);\n        renderer.drawText(scoreText, 10, 10, Color::White, 1);\n    }\n};\n
    "},{"location":"api_reference/core/scene/#performance-considerations","title":"Performance Considerations","text":"
    • Entity limit: MAX_ENTITIES (default 32) can be overridden via compiler flags; plan accordingly
    • Add/Remove: Frequent add/remove operations can be expensive; use object pooling
    • Update order: Entities are updated in add order; consider order for dependencies
    • Collision checks: CollisionSystem automatically handles actor collisions efficiently
    "},{"location":"api_reference/core/scene/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Each entity consumes memory; stay well below the limit
    • Object pooling: Essential for ESP32 to avoid memory fragmentation
    • Scene switching: Clearing and recreating scenes can fragment memory; reuse scenes when possible
    "},{"location":"api_reference/core/scene/#see-also","title":"See Also","text":"
    • Entity - Base entity class
    • Actor - Entity with collision support
    • PhysicsActor - Entity with physics
    • CollisionSystem - Collision detection
    • Manual - Scenes and Entities
    • API Overview
    "},{"location":"api_reference/graphics/camera2d/","title":"Camera2D","text":"

    2D camera for scrolling and viewport control.

    "},{"location":"api_reference/graphics/camera2d/#description","title":"Description","text":"

    Camera2D controls viewport position and enables scrolling by shifting the renderer's display offset. It supports following targets, boundary constraints, and can be used for parallax effects.

    The camera uses a dead-zone system: it only moves when the target is outside a central zone, creating smooth following behavior.

    "},{"location":"api_reference/graphics/camera2d/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    class Camera2D {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/camera2d/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Scenes (for scrolling and camera control)
    "},{"location":"api_reference/graphics/camera2d/#constructors","title":"Constructors","text":""},{"location":"api_reference/graphics/camera2d/#camera2dint-viewportwidth-int-viewportheight","title":"Camera2D(int viewportWidth, int viewportHeight)","text":"

    Creates a new camera with specified viewport dimensions.

    Parameters: - viewportWidth (int): Width of the viewport in pixels - viewportHeight (int): Height of the viewport in pixels

    Notes: - Viewport size should match display size - Camera position starts at (0, 0) - No boundaries set by default (camera can move anywhere)

    Example:

    #include \"graphics/Camera2D.h\"\n\n// Create camera matching display size\npixelroot32::graphics::Camera2D camera(128, 128);\n\n// Or get from renderer\nint width = renderer.getWidth();\nint height = renderer.getHeight();\npixelroot32::graphics::Camera2D camera(width, height);\n

    "},{"location":"api_reference/graphics/camera2d/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/graphics/camera2d/#void-setpositionfloat-x-float-y","title":"void setPosition(float x, float y)","text":"

    Sets the camera position directly.

    Parameters: - x (float): X position in world space - y (float): Y position in world space

    Returns: - void

    Notes: - Position is clamped to boundaries if set - Use for direct camera control or cutscenes - Overrides any following behavior

    Example:

    camera.setPosition(100.0f, 200.0f);\n

    "},{"location":"api_reference/graphics/camera2d/#void-setboundsfloat-minx-float-maxx","title":"void setBounds(float minX, float maxX)","text":"

    Sets horizontal boundaries for the camera.

    Parameters: - minX (float): Minimum X position - maxX (float): Maximum X position

    Returns: - void

    Notes: - Camera position is clamped to these bounds - Use to prevent camera from going outside level bounds - Set both horizontal and vertical bounds for full constraint

    Example:

    // Level is 512 pixels wide, camera viewport is 128\n// Prevent camera from showing outside level\ncamera.setBounds(0.0f, 512.0f - 128.0f);\n

    "},{"location":"api_reference/graphics/camera2d/#void-setverticalboundsfloat-miny-float-maxy","title":"void setVerticalBounds(float minY, float maxY)","text":"

    Sets vertical boundaries for the camera.

    Parameters: - minY (float): Minimum Y position - maxY (float): Maximum Y position

    Returns: - void

    Notes: - Camera position is clamped to these bounds - Use to prevent camera from going outside level bounds vertically

    Example:

    // Level is 512 pixels tall, camera viewport is 128\ncamera.setVerticalBounds(0.0f, 512.0f - 128.0f);\n

    "},{"location":"api_reference/graphics/camera2d/#void-followtargetfloat-targetx","title":"void followTarget(float targetX)","text":"

    Makes the camera follow a target horizontally only.

    Parameters: - targetX (float): X position of the target to follow

    Returns: - void

    Notes: - Camera follows target with dead-zone behavior - Only horizontal movement; vertical position unchanged - Useful for side-scrolling games

    Example:

    void update(unsigned long deltaTime) override {\n    // Update player position\n    player->update(deltaTime);\n\n    // Camera follows player horizontally\n    camera.followTarget(player->x);\n    camera.apply(renderer);\n}\n

    "},{"location":"api_reference/graphics/camera2d/#void-followtargetfloat-targetx-float-targety","title":"void followTarget(float targetX, float targetY)","text":"

    Makes the camera follow a target in both axes.

    Parameters: - targetX (float): X position of the target to follow - targetY (float): Y position of the target to follow

    Returns: - void

    Notes: - Camera follows target with dead-zone behavior - Both horizontal and vertical following - Useful for top-down or platformer games

    Example:

    void update(unsigned long deltaTime) override {\n    player->update(deltaTime);\n\n    // Camera follows player in both axes\n    camera.followTarget(player->x, player->y);\n    camera.apply(renderer);\n}\n

    "},{"location":"api_reference/graphics/camera2d/#float-getx-const","title":"float getX() const","text":"

    Gets the current X position of the camera.

    Returns: - float: Current X position in world space

    Example:

    float cameraX = camera.getX();\n

    "},{"location":"api_reference/graphics/camera2d/#float-gety-const","title":"float getY() const","text":"

    Gets the current Y position of the camera.

    Returns: - float: Current Y position in world space

    Example:

    float cameraY = camera.getY();\n

    "},{"location":"api_reference/graphics/camera2d/#void-applyrenderer-renderer-const","title":"void apply(Renderer& renderer) const","text":"

    Applies the camera's offset to the renderer.

    Parameters: - renderer (Renderer&): The renderer to apply the camera offset to

    Returns: - void

    Notes: - Sets the renderer's display offset based on camera position - Should be called before drawing world elements - Negative offset is applied (camera moves right = world moves left)

    Example:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Apply camera offset\n    camera.apply(renderer);\n\n    // Draw world (offset applied automatically)\n    renderer.drawTileMap(levelMap, 0, 0, Color::White);\n    renderer.drawSprite(playerSprite, playerX, playerY, Color::White);\n\n    // UI elements (not affected by camera)\n    renderer.setDisplayOffset(0, 0);  // Reset for UI\n    renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n}\n

    "},{"location":"api_reference/graphics/camera2d/#dead-zone-following","title":"Dead-Zone Following","text":"

    The camera uses a dead-zone system for smooth following:

    • Dead zone: Central area where camera doesn't move
    • Following: Camera moves only when target leaves dead zone
    • Smooth: Creates natural, non-jarring camera movement

    Example:

    // Camera follows player with dead zone\nvoid update(unsigned long deltaTime) override {\n    player->update(deltaTime);\n\n    // Camera follows (dead zone handled internally)\n    camera.followTarget(player->x, player->y);\n}\n

    "},{"location":"api_reference/graphics/camera2d/#usage-example","title":"Usage Example","text":"
    #include \"graphics/Camera2D.h\"\n\nclass GameScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    PlayerActor* player;\n    TileMap levelMap;\n\npublic:\n    void init() override {\n        // Create camera matching display size\n        auto& renderer = engine.getRenderer();\n        camera = pixelroot32::graphics::Camera2D(\n            renderer.getWidth(), \n            renderer.getHeight()\n        );\n\n        // Set level boundaries\n        // Level is 512x512, viewport is 128x128\n        camera.setBounds(0.0f, 512.0f - 128.0f);\n        camera.setVerticalBounds(0.0f, 512.0f - 128.0f);\n\n        // Create player\n        player = new PlayerActor(64, 64);\n        addEntity(player);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Camera follows player\n        camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Apply camera\n        camera.apply(renderer);\n\n        // Draw world (camera offset applied)\n        renderer.drawTileMap(levelMap, 0, 0, Color::White);\n\n        // Draw entities (Scene::draw handles this)\n        Scene::draw(renderer);\n\n        // Reset offset for UI\n        renderer.setDisplayOffset(0, 0);\n        renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n    }\n};\n
    "},{"location":"api_reference/graphics/camera2d/#parallax-scrolling","title":"Parallax Scrolling","text":"

    Use multiple cameras or manual offset for parallax:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Background layer (slow parallax)\n    float bgOffsetX = camera.getX() * 0.5f;  // 50% speed\n    renderer.setDisplayOffset(-bgOffsetX, 0);\n    renderer.drawTileMap(backgroundMap, 0, 0, Color::White);\n\n    // Midground layer (normal speed)\n    camera.apply(renderer);\n    renderer.drawTileMap(midgroundMap, 0, 0, Color::White);\n\n    // Foreground (entities, normal speed)\n    Scene::draw(renderer);\n\n    // UI (no offset)\n    renderer.setDisplayOffset(0, 0);\n    renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n}\n
    "},{"location":"api_reference/graphics/camera2d/#performance-considerations","title":"Performance Considerations","text":"
    • Apply frequency: apply() is fast; safe to call every frame
    • Boundary checks: Boundary clamping is efficient
    • Following: Dead-zone calculations are lightweight
    "},{"location":"api_reference/graphics/camera2d/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Float math: Uses floating point; acceptable but integer math would be faster
    • Memory: Camera is small (few floats); minimal memory usage
    "},{"location":"api_reference/graphics/camera2d/#see-also","title":"See Also","text":"
    • Renderer - Rendering system
    • Manual - Cameras and Scrolling
    • API Overview
    "},{"location":"api_reference/graphics/color/","title":"Color","text":"

    Color constants and palette management system.

    "},{"location":"api_reference/graphics/color/#description","title":"Description","text":"

    The Color enum provides color constants that map to palette indices. The engine supports both legacy mode (single global palette) and dual palette mode (separate palettes for backgrounds and sprites).

    Colors are resolved to 16-bit RGB565 values based on the active palette(s).

    "},{"location":"api_reference/graphics/color/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    enum class Color : uint8_t {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/color/#color-enum-values","title":"Color Enum Values","text":""},{"location":"api_reference/graphics/color/#standard-colors-pr32-palette-indices","title":"Standard Colors (PR32 Palette Indices)","text":"
    • Color::Black (0)
    • Color::White (1)
    • Color::Navy (2)
    • Color::Blue (3)
    • Color::Cyan (4)
    • Color::DarkGreen (5)
    • Color::Green (6)
    • Color::LightGreen (7)
    • Color::Yellow (8)
    • Color::Orange (9)
    • Color::LightRed (10)
    • Color::Red (11)
    • Color::DarkRed (12)
    • Color::Purple (13)
    • Color::Magenta (14)
    • Color::Gray (15)
    "},{"location":"api_reference/graphics/color/#color-aliases","title":"Color Aliases","text":"

    For compatibility, several aliases map to the closest available color:

    • Color::DarkBlue \u2192 Navy
    • Color::LightBlue \u2192 Blue
    • Color::Teal \u2192 Cyan
    • Color::Olive \u2192 DarkGreen
    • Color::Gold \u2192 Yellow
    • Color::Brown \u2192 DarkRed
    • Color::Pink \u2192 Magenta
    • Color::LightPurple \u2192 Magenta
    • Color::Maroon \u2192 DarkRed
    • Color::MidGray \u2192 Gray
    • Color::LightGray \u2192 Gray
    • Color::DarkGray \u2192 Gray
    • Color::Silver \u2192 Gray
    "},{"location":"api_reference/graphics/color/#special-colors","title":"Special Colors","text":"
    • Color::Transparent (255): Not a real color; must be handled by renderer (results in no-op)
    • Color::DebugRed \u2192 Red
    • Color::DebugGreen \u2192 Green
    • Color::DebugBlue \u2192 Blue
    "},{"location":"api_reference/graphics/color/#palettetype-enum","title":"PaletteType Enum","text":"

    Built-in palette types:

    • PaletteType::NES: NES color palette
    • PaletteType::GB: Game Boy (4 shades of green)
    • PaletteType::GBC: Game Boy Color palette
    • PaletteType::PICO8: PICO-8 palette
    • PaletteType::PR32: PixelRoot32 default palette
    "},{"location":"api_reference/graphics/color/#palettecontext-enum","title":"PaletteContext Enum","text":"

    Context for palette selection in dual palette mode:

    • PaletteContext::Background: For backgrounds, tilemaps, and background primitives
    • PaletteContext::Sprite: For sprites, characters, and gameplay elements
    "},{"location":"api_reference/graphics/color/#palette-functions","title":"Palette Functions","text":""},{"location":"api_reference/graphics/color/#void-setpalettepalettetype-palette","title":"void setPalette(PaletteType palette)","text":"

    Selects the active color palette (legacy mode). Sets both background and sprite palettes to the same value.

    Parameters: - palette (PaletteType): The palette to use

    Notes: - Does not enable dual palette mode - All rendering uses the same palette - Use for simple games with single palette

    Example:

    pixelroot32::graphics::setPalette(pixelroot32::graphics::PaletteType::NES);\n

    "},{"location":"api_reference/graphics/color/#void-setcustompaletteconst-uint16_t-palette","title":"void setCustomPalette(const uint16_t* palette)","text":"

    Sets a custom color palette (legacy mode). Sets both background and sprite palettes to the same value.

    Parameters: - palette (const uint16_t*): Pointer to an array of 16 uint16_t RGB565 color values

    Notes: - Array must remain valid (use static/global storage) - Engine does not copy the palette - Does not enable dual palette mode

    Example:

    static const uint16_t MY_PALETTE[16] = {\n    0x0000,  // Black\n    0xFFFF,  // White\n    0x001F,  // Blue\n    // ... 13 more colors\n};\n\npixelroot32::graphics::setCustomPalette(MY_PALETTE);\n

    "},{"location":"api_reference/graphics/color/#void-enabledualpalettemodebool-enable","title":"void enableDualPaletteMode(bool enable)","text":"

    Enables or disables dual palette mode.

    Parameters: - enable (bool): true to enable dual palette mode, false for legacy mode

    Notes: - When enabled: backgrounds and sprites use separate palettes - When disabled: single palette for all rendering (legacy mode)

    Example:

    pixelroot32::graphics::enableDualPaletteMode(true);\n

    "},{"location":"api_reference/graphics/color/#void-setbackgroundpalettepalettetype-palette","title":"void setBackgroundPalette(PaletteType palette)","text":"

    Sets the background palette (for backgrounds, tilemaps, etc.).

    Parameters: - palette (PaletteType): The palette type to use for backgrounds

    Notes: - Only used in dual palette mode - Affects tilemaps, background primitives, etc.

    Example:

    pixelroot32::graphics::enableDualPaletteMode(true);\npixelroot32::graphics::setBackgroundPalette(pixelroot32::graphics::PaletteType::NES);\n

    "},{"location":"api_reference/graphics/color/#void-setspritepalettepalettetype-palette","title":"void setSpritePalette(PaletteType palette)","text":"

    Sets the sprite palette (for sprites, characters, etc.).

    Parameters: - palette (PaletteType): The palette type to use for sprites

    Notes: - Only used in dual palette mode - Affects sprites, characters, gameplay elements

    Example:

    pixelroot32::graphics::setSpritePalette(pixelroot32::graphics::PaletteType::GB);\n

    "},{"location":"api_reference/graphics/color/#void-setdualpalettepalettetype-bgpalette-palettetype-spritepalette","title":"void setDualPalette(PaletteType bgPalette, PaletteType spritePalette)","text":"

    Sets both background and sprite palettes at once. Automatically enables dual palette mode.

    Parameters: - bgPalette (PaletteType): The palette type to use for backgrounds - spritePalette (PaletteType): The palette type to use for sprites

    Example:

    pixelroot32::graphics::setDualPalette(\n    pixelroot32::graphics::PaletteType::NES,  // Background\n    pixelroot32::graphics::PaletteType::GB     // Sprites\n);\n

    "},{"location":"api_reference/graphics/color/#void-setbackgroundcustompaletteconst-uint16_t-palette","title":"void setBackgroundCustomPalette(const uint16_t* palette)","text":"

    Sets a custom background palette.

    Parameters: - palette (const uint16_t*): Pointer to an array of 16 uint16_t RGB565 color values

    Notes: - Array must remain valid (use static/global storage) - Only used in dual palette mode

    Example:

    static const uint16_t BG_PALETTE[16] = { /* ... */ };\npixelroot32::graphics::enableDualPaletteMode(true);\npixelroot32::graphics::setBackgroundCustomPalette(BG_PALETTE);\n

    "},{"location":"api_reference/graphics/color/#void-setspritecustompaletteconst-uint16_t-palette","title":"void setSpriteCustomPalette(const uint16_t* palette)","text":"

    Sets a custom sprite palette.

    Parameters: - palette (const uint16_t*): Pointer to an array of 16 uint16_t RGB565 color values

    Notes: - Array must remain valid (use static/global storage) - Only used in dual palette mode

    Example:

    static const uint16_t SPRITE_PALETTE[16] = { /* ... */ };\npixelroot32::graphics::setSpriteCustomPalette(SPRITE_PALETTE);\n

    "},{"location":"api_reference/graphics/color/#void-setdualcustompaletteconst-uint16_t-bgpalette-const-uint16_t-spritepal","title":"void setDualCustomPalette(const uint16_t bgPalette, const uint16_t spritePal)","text":"

    Sets both background and sprite custom palettes at once. Automatically enables dual palette mode.

    Parameters: - bgPalette (const uint16_t): Pointer to background palette array (16 RGB565 values) - spritePal (const uint16_t): Pointer to sprite palette array (16 RGB565 values)

    Example:

    static const uint16_t BG_PAL[16] = { /* ... */ };\nstatic const uint16_t SPRITE_PAL[16] = { /* ... */ };\npixelroot32::graphics::setDualCustomPalette(BG_PAL, SPRITE_PAL);\n

    "},{"location":"api_reference/graphics/color/#uint16_t-resolvecolorcolor-color","title":"uint16_t resolveColor(Color color)","text":"

    Resolves a Color enum to its corresponding 16-bit color value (legacy mode).

    Parameters: - color (Color): The Color enum value

    Returns: - uint16_t: The 16-bit RGB565 color value

    Notes: - Uses the current active palette (single palette mode) - Color::Transparent must not be resolved (handled by renderer) - Typically called internally by renderer

    Example:

    uint16_t rgb565 = pixelroot32::graphics::resolveColor(pixelroot32::graphics::Color::Red);\n

    "},{"location":"api_reference/graphics/color/#uint16_t-resolvecolorcolor-color-palettecontext-context","title":"uint16_t resolveColor(Color color, PaletteContext context)","text":"

    Resolves a Color enum with context (dual palette mode).

    Parameters: - color (Color): The Color enum value - context (PaletteContext): The palette context (Background or Sprite)

    Returns: - uint16_t: The 16-bit RGB565 color value

    Notes: - Uses the appropriate palette based on context - In legacy mode, context is ignored - Color::Transparent must not be resolved

    Example:

    uint16_t bgColor = pixelroot32::graphics::resolveColor(\n    pixelroot32::graphics::Color::Blue,\n    pixelroot32::graphics::PaletteContext::Background\n);\n

    "},{"location":"api_reference/graphics/color/#rgb565-format","title":"RGB565 Format","text":"

    Colors are stored as 16-bit RGB565 values:

    • Bits 15-11: Red (5 bits, 0-31)
    • Bits 10-5: Green (6 bits, 0-63)
    • Bits 4-0: Blue (5 bits, 0-31)

    Conversion Example:

    // Convert RGB to RGB565\nuint16_t rgb565 = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);\n

    "},{"location":"api_reference/graphics/color/#usage-examples","title":"Usage Examples","text":""},{"location":"api_reference/graphics/color/#legacy-mode-single-palette","title":"Legacy Mode (Single Palette)","text":"
    // Set single palette for all rendering\npixelroot32::graphics::setPalette(pixelroot32::graphics::PaletteType::NES);\n\n// Use colors\nrenderer.drawSprite(sprite, 100, 100, pixelroot32::graphics::Color::Red);\nrenderer.drawFilledRectangle(10, 10, 50, 50, pixelroot32::graphics::Color::Blue);\n
    "},{"location":"api_reference/graphics/color/#dual-palette-mode","title":"Dual Palette Mode","text":"
    // Enable dual palette mode\npixelroot32::graphics::enableDualPaletteMode(true);\n\n// Set different palettes for backgrounds and sprites\npixelroot32::graphics::setBackgroundPalette(pixelroot32::graphics::PaletteType::NES);\npixelroot32::graphics::setSpritePalette(pixelroot32::graphics::PaletteType::GB);\n\n// Or use convenience function\npixelroot32::graphics::setDualPalette(\n    pixelroot32::graphics::PaletteType::NES,\n    pixelroot32::graphics::PaletteType::GB\n);\n
    "},{"location":"api_reference/graphics/color/#custom-palettes","title":"Custom Palettes","text":"
    // Define custom palette (RGB565 values)\nstatic const uint16_t CUSTOM_PALETTE[16] = {\n    0x0000,  // 0: Black\n    0xFFFF,  // 1: White\n    0x001F,  // 2: Blue\n    0x07E0,  // 3: Green\n    0xF800,  // 4: Red\n    // ... 11 more colors\n};\n\n// Use in legacy mode\npixelroot32::graphics::setCustomPalette(CUSTOM_PALETTE);\n\n// Or use in dual palette mode\npixelroot32::graphics::enableDualPaletteMode(true);\npixelroot32::graphics::setBackgroundCustomPalette(CUSTOM_PALETTE);\npixelroot32::graphics::setSpriteCustomPalette(CUSTOM_PALETTE);\n
    "},{"location":"api_reference/graphics/color/#performance-considerations","title":"Performance Considerations","text":"
    • Color resolution: Fast lookup operation
    • Palette switching: Changing palettes is fast (just pointer assignment)
    • Memory: Palettes are stored in flash (const arrays) for best performance
    • Dual mode: Slightly more overhead than legacy mode, but minimal
    "},{"location":"api_reference/graphics/color/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Flash storage: Store custom palettes in flash (const/constexpr)
    • Memory: Palettes are small (16 uint16_t = 32 bytes)
    • Palette switching: Avoid switching palettes every frame
    "},{"location":"api_reference/graphics/color/#see-also","title":"See Also","text":"
    • Renderer - Rendering system
    • Manual - Color Palettes
    • API Overview
    "},{"location":"api_reference/graphics/display_config/","title":"DisplayConfig","text":"

    Configuration settings for initializing the display.

    "},{"location":"api_reference/graphics/display_config/#description","title":"Description","text":"

    DisplayConfig holds display parameters used by the renderer and camera to draw correctly on the target device. It defines the display type, dimensions, rotation, and creates the appropriate DrawSurface implementation for the platform.

    "},{"location":"api_reference/graphics/display_config/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    struct DisplayConfig {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/display_config/#displaytype-enum","title":"DisplayType Enum","text":"

    Supported display types:

    • DisplayType::ST7789: 240x240 TFT display
    • DisplayType::ST7735: 128x128 TFT display
    • DisplayType::NONE: For SDL2 native (no driver needed)
    "},{"location":"api_reference/graphics/display_config/#structure","title":"Structure","text":""},{"location":"api_reference/graphics/display_config/#displaytype-type","title":"DisplayType type","text":"

    The type of display.

    Type: DisplayType enum

    Access: Read-write

    Notes: - Determines which driver to use (ESP32) - NONE for Native/SDL2 platform

    "},{"location":"api_reference/graphics/display_config/#int-rotation","title":"int rotation","text":"

    Display rotation in degrees.

    Type: int

    Access: Read-write

    Default: 0

    Notes: - Common values: 0, 90, 180, 270 - Rotation is applied during initialization - Some displays may not support all rotations

    "},{"location":"api_reference/graphics/display_config/#uint16_t-width","title":"uint16_t width","text":"

    Display width in pixels.

    Type: uint16_t

    Access: Read-write

    Default: 240

    Notes: - Should match actual display width - Used for viewport and camera calculations

    "},{"location":"api_reference/graphics/display_config/#uint16_t-height","title":"uint16_t height","text":"

    Display height in pixels.

    Type: uint16_t

    Access: Read-write

    Default: 240

    Notes: - Should match actual display height - Used for viewport and camera calculations

    "},{"location":"api_reference/graphics/display_config/#int-xoffset","title":"int xOffset","text":"

    X offset for display alignment.

    Type: int

    Access: Read-write

    Default: 0

    Notes: - Used to adjust display position - Some displays need offset for proper alignment

    "},{"location":"api_reference/graphics/display_config/#int-yoffset","title":"int yOffset","text":"

    Y offset for display alignment.

    Type: int

    Access: Read-write

    Default: 0

    Notes: - Used to adjust display position - Some displays need offset for proper alignment

    "},{"location":"api_reference/graphics/display_config/#drawsurface-getdrawsurface-const","title":"DrawSurface& getDrawSurface() const","text":"

    Gets the underlying DrawSurface implementation.

    Returns: - DrawSurface&: Reference to the DrawSurface

    Notes: - Advanced usage: typically not needed - Provides access to low-level display driver - Platform-specific implementation

    "},{"location":"api_reference/graphics/display_config/#constructors","title":"Constructors","text":""},{"location":"api_reference/graphics/display_config/#displayconfigdisplaytype-type-const-int-rot-0-uint16_t-w-240-uint16_t-h-240-const-int-xoffset-0-const-int-yoffset-0","title":"DisplayConfig(DisplayType type, const int rot = 0, uint16_t w = 240, uint16_t h = 240, const int xOffset = 0, const int yOffset = 0)","text":"

    Constructs a DisplayConfig with specified parameters.

    Parameters: - type (DisplayType): The display type - rot (int, optional): Rotation in degrees. Default: 0 - w (uint16_t, optional): Width in pixels. Default: 240 - h (uint16_t, optional): Height in pixels. Default: 240 - xOffset (int, optional): X offset. Default: 0 - yOffset (int, optional): Y offset. Default: 0

    Notes: - Automatically creates the appropriate DrawSurface for the platform - ESP32: Creates TFT_eSPI_Drawer based on display type - Native: Creates SDL2_Drawer

    Example:

    // ST7789 display, 240x240, no rotation\npixelroot32::graphics::DisplayConfig config(\n    pixelroot32::graphics::DisplayType::ST7789\n);\n\n// ST7735 display, 128x128, rotated 90 degrees\npixelroot32::graphics::DisplayConfig config(\n    pixelroot32::graphics::DisplayType::ST7735,\n    90,   // rotation\n    128,  // width\n    128   // height\n);\n\n// Native/SDL2 (no specific display type)\npixelroot32::graphics::DisplayConfig config(\n    pixelroot32::graphics::DisplayType::NONE,\n    0,    // rotation\n    128,  // width\n    128   // height\n);\n

    "},{"location":"api_reference/graphics/display_config/#usage-examples","title":"Usage Examples","text":""},{"location":"api_reference/graphics/display_config/#esp32-with-st7789","title":"ESP32 with ST7789","text":"
    #ifdef PLATFORM_ESP32\n#include \"graphics/DisplayConfig.h\"\n\nvoid setup() {\n    // Configure ST7789 display (240x240)\n    pixelroot32::graphics::DisplayConfig displayConfig(\n        pixelroot32::graphics::DisplayType::ST7789,\n        0,    // rotation\n        240,  // width\n        240   // height\n    );\n\n    // Use with Engine\n    pixelroot32::core::Engine engine(displayConfig);\n    engine.init();\n    engine.run();\n}\n#endif\n
    "},{"location":"api_reference/graphics/display_config/#esp32-with-st7735","title":"ESP32 with ST7735","text":"
    #ifdef PLATFORM_ESP32\n    // Configure ST7735 display (128x128)\n    pixelroot32::graphics::DisplayConfig displayConfig(\n        pixelroot32::graphics::DisplayType::ST7735,\n        0,    // rotation\n        128,  // width\n        128   // height\n    );\n#endif\n
    "},{"location":"api_reference/graphics/display_config/#nativesdl2","title":"Native/SDL2","text":"
    #ifdef PLATFORM_NATIVE\n    // Native display (SDL2 window)\n    pixelroot32::graphics::DisplayConfig displayConfig(\n        pixelroot32::graphics::DisplayType::NONE,\n        0,    // rotation\n        128,  // width\n        128   // height\n    );\n#endif\n
    "},{"location":"api_reference/graphics/display_config/#platform-agnostic-setup","title":"Platform-Agnostic Setup","text":"
    #include \"graphics/DisplayConfig.h\"\n#include \"core/Engine.h\"\n\nvoid setup() {\n    pixelroot32::graphics::DisplayConfig displayConfig;\n\n    #ifdef PLATFORM_ESP32\n        displayConfig.type = pixelroot32::graphics::DisplayType::ST7789;\n        displayConfig.width = 240;\n        displayConfig.height = 240;\n    #elif PLATFORM_NATIVE\n        displayConfig.type = pixelroot32::graphics::DisplayType::NONE;\n        displayConfig.width = 128;\n        displayConfig.height = 128;\n    #endif\n\n    displayConfig.rotation = 0;\n\n    pixelroot32::core::Engine engine(displayConfig);\n    engine.init();\n    engine.run();\n}\n
    "},{"location":"api_reference/graphics/display_config/#display-type-details","title":"Display Type Details","text":""},{"location":"api_reference/graphics/display_config/#st7789","title":"ST7789","text":"
    • Resolution: Typically 240x240 or 240x320
    • Interface: SPI
    • Driver: TFT_eSPI
    • Common sizes: 240x240, 240x320
    "},{"location":"api_reference/graphics/display_config/#st7735","title":"ST7735","text":"
    • Resolution: Typically 128x128 or 128x160
    • Interface: SPI
    • Driver: TFT_eSPI
    • Common sizes: 128x128, 128x160
    "},{"location":"api_reference/graphics/display_config/#none-native","title":"NONE (Native)","text":"
    • Platform: Native/SDL2
    • Driver: SDL2_Drawer
    • Resolution: Configurable (any size)
    • Window: Creates SDL2 window
    "},{"location":"api_reference/graphics/display_config/#rotation","title":"Rotation","text":"

    Display rotation values:

    • 0: Normal orientation
    • 90: Rotated 90 degrees clockwise
    • 180: Rotated 180 degrees
    • 270: Rotated 90 degrees counter-clockwise

    Notes: - Rotation affects coordinate system - Some displays may not support all rotations - Test rotation on your specific hardware

    "},{"location":"api_reference/graphics/display_config/#performance-considerations","title":"Performance Considerations","text":"
    • Initialization: Display initialization happens once at startup
    • Driver selection: Automatic based on platform and type
    • Memory: DisplayConfig is small (few fields)
    "},{"location":"api_reference/graphics/display_config/#esp32-considerations","title":"ESP32 Considerations","text":""},{"location":"api_reference/graphics/display_config/#tft_espi-configuration","title":"TFT_eSPI Configuration","text":"

    DisplayConfig uses TFT_eSPI driver. Additional configuration may be needed in platformio.ini:

    build_flags =\n    -DUSER_SETUP_LOADED=1\n    -DST7789_DRIVER=1\n    -DTFT_WIDTH=240\n    -DTFT_HEIGHT=240\n    # ... pin configuration\n
    "},{"location":"api_reference/graphics/display_config/#pin-configuration","title":"Pin Configuration","text":"

    GPIO pins must be configured separately (not in DisplayConfig):

    • MOSI: Data pin
    • SCLK: Clock pin
    • DC: Data/Command pin
    • RST: Reset pin
    • CS: Chip select pin (optional)
    "},{"location":"api_reference/graphics/display_config/#see-also","title":"See Also","text":"
    • Renderer - Rendering system
    • Camera2D - Camera that uses display dimensions
    • Manual - Platforms and Drivers
    • API Overview
    "},{"location":"api_reference/graphics/font/","title":"Font","text":"

    Descriptor for a bitmap font using 1bpp sprites.

    "},{"location":"api_reference/graphics/font/#description","title":"Description","text":"

    A Font contains an array of Sprite structures, one for each character in the font's character set. Each glyph is rendered as a 1bpp sprite, allowing consistent rendering across platforms.

    The font uses fixed-width glyphs for simplicity and performance. All glyphs share the same width and height, with spacing between characters controlled by the spacing field.

    "},{"location":"api_reference/graphics/font/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    struct Font {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/font/#structure","title":"Structure","text":""},{"location":"api_reference/graphics/font/#const-sprite-glyphs","title":"const Sprite* glyphs","text":"

    Array of sprites, one per character (indexed by character code - firstChar).

    Type: const Sprite*

    Access: Read-only

    Notes: - Array must contain sprites for all characters from firstChar to lastChar - Each sprite represents one character glyph - Should be stored in flash (const/constexpr) for best performance

    Example:

    static const Sprite FONT_GLYPHS[] = {\n    spaceGlyph,    // Index 0 = ' ' (firstChar = 32)\n    exclamationGlyph, // Index 1 = '!'\n    // ... more glyphs\n};\n\nstatic const Font myFont = {\n    FONT_GLYPHS,\n    32,  // firstChar\n    126, // lastChar\n    5,   // glyphWidth\n    7,   // glyphHeight\n    1,   // spacing\n    8    // lineHeight\n};\n

    "},{"location":"api_reference/graphics/font/#uint8_t-firstchar","title":"uint8_t firstChar","text":"

    First character code in the font.

    Type: uint8_t

    Access: Read-only

    Default: Typically 32 (space character)

    Notes: - ASCII code of the first character - Common: 32 (space ' ') for ASCII fonts - Glyphs array starts at index 0 for this character

    Example:

    firstChar = 32;  // Starts at space character\n

    "},{"location":"api_reference/graphics/font/#uint8_t-lastchar","title":"uint8_t lastChar","text":"

    Last character code in the font.

    Type: uint8_t

    Access: Read-only

    Default: Typically 126 (tilde '~')

    Notes: - ASCII code of the last character - Common: 126 (tilde '~') for full ASCII fonts - Glyphs array must contain (lastChar - firstChar + 1) sprites

    Example:

    lastChar = 126;  // Ends at tilde character\n// Font contains characters 32-126 (95 characters)\n

    "},{"location":"api_reference/graphics/font/#uint8_t-glyphwidth","title":"uint8_t glyphWidth","text":"

    Fixed width of each glyph in pixels.

    Type: uint8_t

    Access: Read-only

    Notes: - All glyphs must have the same width - Typical values: 5, 6, 8 pixels - Smaller = more characters per line, less readable

    Example:

    glyphWidth = 5;  // 5 pixels wide (like FONT_5X7)\n

    "},{"location":"api_reference/graphics/font/#uint8_t-glyphheight","title":"uint8_t glyphHeight","text":"

    Fixed height of each glyph in pixels.

    Type: uint8_t

    Access: Read-only

    Notes: - All glyphs must have the same height - Typical values: 7, 8, 10 pixels - Smaller = more lines, less readable

    Example:

    glyphHeight = 7;  // 7 pixels tall (like FONT_5X7)\n

    "},{"location":"api_reference/graphics/font/#uint8_t-spacing","title":"uint8_t spacing","text":"

    Horizontal spacing between characters in pixels.

    Type: uint8_t

    Access: Read-only

    Default: Typically 1

    Notes: - Space added between characters - 0 = no spacing (characters touch) - 1 = 1 pixel gap (common) - Higher values = more readable but wider text

    Example:

    spacing = 1;  // 1 pixel between characters\n

    "},{"location":"api_reference/graphics/font/#uint8_t-lineheight","title":"uint8_t lineHeight","text":"

    Total line height including vertical spacing.

    Type: uint8_t

    Access: Read-only

    Notes: - Should be glyphHeight + verticalSpacing - Used for line breaks and multi-line text - Typical: glyphHeight + 1 or glyphHeight + 2

    Example:

    lineHeight = 8;  // 7 pixel glyph + 1 pixel spacing\n

    "},{"location":"api_reference/graphics/font/#built-in-fonts","title":"Built-in Fonts","text":""},{"location":"api_reference/graphics/font/#font_5x7","title":"FONT_5X7","text":"

    Standard 5x7 pixel font (built-in).

    Properties: - Width: 5 pixels - Height: 7 pixels - Characters: Typically ASCII 32-126 - Spacing: 1 pixel - Line height: 8 pixels

    Usage:

    #include \"graphics/Font.h\"\n\nrenderer.drawText(\"Hello\", 10, 10, Color::White, 1, &FONT_5X7);\n

    "},{"location":"api_reference/graphics/font/#creating-custom-fonts","title":"Creating Custom Fonts","text":""},{"location":"api_reference/graphics/font/#step-1-create-glyph-sprites","title":"Step 1: Create Glyph Sprites","text":"
    // Space character (ASCII 32)\nstatic const uint16_t SPACE_GLYPH[] = {\n    0b00000,\n    0b00000,\n    0b00000,\n    0b00000,\n    0b00000,\n    0b00000,\n    0b00000\n};\n\n// 'A' character (ASCII 65)\nstatic const uint16_t A_GLYPH[] = {\n    0b00100,\n    0b01010,\n    0b10001,\n    0b11111,\n    0b10001,\n    0b10001,\n    0b00000\n};\n\n// ... more glyphs\n
    "},{"location":"api_reference/graphics/font/#step-2-create-glyph-array","title":"Step 2: Create Glyph Array","text":"
    static const Sprite FONT_GLYPHS[] = {\n    {SPACE_GLYPH, 5, 7},  // Index 0 = ' ' (32)\n    // ... more glyphs\n    {A_GLYPH, 5, 7},      // Index 33 = 'A' (65)\n    // ... more glyphs\n};\n
    "},{"location":"api_reference/graphics/font/#step-3-create-font-structure","title":"Step 3: Create Font Structure","text":"
    static const Font MY_FONT = {\n    FONT_GLYPHS,  // glyphs array\n    32,           // firstChar (space)\n    126,          // lastChar (tilde)\n    5,            // glyphWidth\n    7,            // glyphHeight\n    1,            // spacing\n    8             // lineHeight\n};\n
    "},{"location":"api_reference/graphics/font/#step-4-use-font","title":"Step 4: Use Font","text":"
    renderer.drawText(\"Hello\", 10, 10, Color::White, 1, &MY_FONT);\n
    "},{"location":"api_reference/graphics/font/#usage-example","title":"Usage Example","text":"
    #include \"graphics/Font.h\"\n#include \"graphics/Renderer.h\"\n\n// Using built-in font\nvoid draw(Renderer& renderer) override {\n    // Default font\n    renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n\n    // Explicit font\n    renderer.drawText(\"Score: 100\", 10, 30, Color::White, 1, &FONT_5X7);\n\n    // Centered text with font\n    renderer.drawTextCentered(\"Game Over\", 64, Color::Yellow, 2, &FONT_5X7);\n}\n\n// Custom font example\nstatic const uint16_t CUSTOM_GLYPHS[][7] = {\n    // Space, A, B, C, etc.\n};\n\nstatic const Sprite CUSTOM_SPRITES[] = {\n    {CUSTOM_GLYPHS[0], 6, 8},  // Space\n    {CUSTOM_GLYPHS[1], 6, 8},  // A\n    // ... more\n};\n\nstatic const Font CUSTOM_FONT = {\n    CUSTOM_SPRITES,\n    32,   // firstChar\n    90,   // lastChar (A-Z only)\n    6,    // width\n    8,    // height\n    1,    // spacing\n    9     // lineHeight\n};\n\nvoid draw(Renderer& renderer) override {\n    renderer.drawText(\"CUSTOM\", 10, 10, Color::White, 1, &CUSTOM_FONT);\n}\n
    "},{"location":"api_reference/graphics/font/#text-sizing","title":"Text Sizing","text":"

    Calculate text dimensions:

    int getTextWidth(const Font* font, const char* text) {\n    if (!font || !text) return 0;\n\n    int width = 0;\n    for (int i = 0; text[i] != '\\0'; i++) {\n        if (text[i] >= font->firstChar && text[i] <= font->lastChar) {\n            width += font->glyphWidth + font->spacing;\n        }\n    }\n    return width - font->spacing;  // Remove last spacing\n}\n\nint getTextHeight(const Font* font) {\n    return font ? font->lineHeight : 8;\n}\n
    "},{"location":"api_reference/graphics/font/#performance-considerations","title":"Performance Considerations","text":"
    • Font storage: Store fonts in flash (const/constexpr) for best performance
    • Glyph lookup: Fast array access (character code - firstChar)
    • Fixed width: Fixed-width fonts are faster than variable-width
    • Font switching: Changing fonts is fast (just pointer assignment)
    "},{"location":"api_reference/graphics/font/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Store font data in flash, not RAM
    • Font size: Larger fonts use more flash memory
    • Character range: Limit character range to save memory if not needed
    "},{"location":"api_reference/graphics/font/#see-also","title":"See Also","text":"
    • Renderer - Rendering system that uses fonts
    • Sprite - Sprite structure used for glyphs
    • Manual - Basic Rendering
    • API Overview
    "},{"location":"api_reference/graphics/renderer/","title":"Renderer","text":"

    High-level graphics rendering system for drawing shapes, text, sprites, and tilemaps.

    "},{"location":"api_reference/graphics/renderer/#description","title":"Description","text":"

    The Renderer class provides a unified API for drawing shapes, text, and images. It abstracts the underlying hardware implementation (DrawSurface) and manages display configuration, including rotation and offsets.

    The renderer uses integer-only math for optimal performance on ESP32 and supports multiple sprite formats (1bpp, 2bpp, 4bpp) and multi-layer sprites.

    "},{"location":"api_reference/graphics/renderer/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    class Renderer {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/renderer/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Engine (manages renderer instance)
    "},{"location":"api_reference/graphics/renderer/#constructors","title":"Constructors","text":""},{"location":"api_reference/graphics/renderer/#rendererconst-displayconfig-config","title":"Renderer(const DisplayConfig& config)","text":"

    Constructs the Renderer with a specific display configuration.

    Parameters: - config (const DisplayConfig&): The display configuration settings (width, height, rotation, etc.)

    Example:

    #include \"graphics/Renderer.h\"\n#include \"graphics/DisplayConfig.h\"\n\npixelroot32::graphics::DisplayConfig config;\nconfig.width = 128;\nconfig.height = 128;\nconfig.rotation = 0;\n\npixelroot32::graphics::Renderer renderer(config);\nrenderer.init();\n

    "},{"location":"api_reference/graphics/renderer/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/graphics/renderer/#void-init","title":"void init()","text":"

    Initializes the renderer and the underlying draw surface.

    Returns: - void

    Notes: - Must be called after construction and before any drawing operations - Initializes the platform-specific DrawSurface implementation - Safe to call multiple times (idempotent)

    Example:

    Renderer renderer(displayConfig);\nrenderer.init();  // Initialize before use\n

    "},{"location":"api_reference/graphics/renderer/#void-beginframe","title":"void beginFrame()","text":"

    Prepares the buffer for a new frame (clears screen).

    Returns: - void

    Notes: - Should be called once at the start of each frame - Clears the display buffer - Typically called automatically by Engine, but can be called manually

    Example:

    void draw(Renderer& renderer) override {\n    renderer.beginFrame();\n    // Draw everything...\n    renderer.endFrame();\n}\n

    "},{"location":"api_reference/graphics/renderer/#void-endframe","title":"void endFrame()","text":"

    Finalizes the frame and sends the buffer to the display.

    Returns: - void

    Notes: - Should be called once at the end of each frame - Sends the completed frame buffer to the display - Typically called automatically by Engine, but can be called manually

    "},{"location":"api_reference/graphics/renderer/#drawsurface-getdrawsurface","title":"DrawSurface& getDrawSurface()","text":"

    Gets the underlying DrawSurface implementation.

    Returns: - DrawSurface&: Reference to the DrawSurface

    Notes: - Advanced usage: typically not needed unless implementing custom drawing - Provides low-level access to the display driver

    "},{"location":"api_reference/graphics/renderer/#void-drawtextconst-char-text-int16_t-x-int16_t-y-color-color-uint8_t-size","title":"void drawText(const char* text, int16_t x, int16_t y, Color color, uint8_t size)","text":"

    Draws a string of text using the default font.

    Parameters: - text (const char*): The text to draw (null-terminated string) - x (int16_t): X coordinate (top-left corner of text) - y (int16_t): Y coordinate (top-left corner of text) - color (Color): Text color - size (uint8_t): Text size multiplier (1 = normal, 2 = double, etc.)

    Performance Notes: - Efficient for small amounts of text - Avoid calling in tight loops with long strings - Use static buffers for text formatting

    Example:

    renderer.drawText(\"Hello World\", 10, 10, Color::White, 1);\nrenderer.drawText(\"Score: 100\", 10, 30, Color::Yellow, 2);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawtextconst-char-text-int16_t-x-int16_t-y-color-color-uint8_t-size-const-font-font","title":"void drawText(const char text, int16_t x, int16_t y, Color color, uint8_t size, const Font font)","text":"

    Draws a string of text using a specific font.

    Parameters: - text (const char): The text to draw - x (int16_t): X coordinate - y (int16_t): Y coordinate - color (Color): Text color - size (uint8_t): Text size multiplier - font (const Font): Pointer to the font to use. If nullptr, uses the default font

    Example:

    const Font* customFont = &FONT_5X7;\nrenderer.drawText(\"Custom Font\", 10, 10, Color::White, 1, customFont);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawtextcenteredconst-char-text-int16_t-y-color-color-uint8_t-size","title":"void drawTextCentered(const char* text, int16_t y, Color color, uint8_t size)","text":"

    Draws text centered horizontally at a given Y coordinate using the default font.

    Parameters: - text (const char*): The text to draw - y (int16_t): Y coordinate - color (Color): Text color - size (uint8_t): Text size

    Example:

    renderer.drawTextCentered(\"Game Over\", 64, Color::White, 2);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawtextcenteredconst-char-text-int16_t-y-color-color-uint8_t-size-const-font-font","title":"void drawTextCentered(const char text, int16_t y, Color color, uint8_t size, const Font font)","text":"

    Draws text centered horizontally at a given Y coordinate using a specific font.

    Parameters: - text (const char): The text to draw - y (int16_t): Y coordinate - color (Color): Text color - size (uint8_t): Text size - font (const Font): Pointer to the font to use. If nullptr, uses the default font

    "},{"location":"api_reference/graphics/renderer/#void-drawfilledcircleint-x-int-y-int-radius-color-color","title":"void drawFilledCircle(int x, int y, int radius, Color color)","text":"

    Draws a filled circle.

    Parameters: - x (int): Center X coordinate - y (int): Center Y coordinate - radius (int): Radius of the circle in pixels - color (Color): Fill color

    Example:

    renderer.drawFilledCircle(64, 64, 20, Color::Red);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawcircleint-x-int-y-int-radius-color-color","title":"void drawCircle(int x, int y, int radius, Color color)","text":"

    Draws a circle outline.

    Parameters: - x (int): Center X coordinate - y (int): Center Y coordinate - radius (int): Radius of the circle in pixels - color (Color): Outline color

    Example:

    renderer.drawCircle(64, 64, 20, Color::White);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawrectangleint-x-int-y-int-width-int-height-color-color","title":"void drawRectangle(int x, int y, int width, int height, Color color)","text":"

    Draws a rectangle outline.

    Parameters: - x (int): Top-left X coordinate - y (int): Top-left Y coordinate - width (int): Width of the rectangle in pixels - height (int): Height of the rectangle in pixels - color (Color): Outline color

    Example:

    renderer.drawRectangle(10, 10, 100, 50, Color::Blue);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawfilledrectangleint-x-int-y-int-width-int-height-color-color","title":"void drawFilledRectangle(int x, int y, int width, int height, Color color)","text":"

    Draws a filled rectangle.

    Parameters: - x (int): Top-left X coordinate - y (int): Top-left Y coordinate - width (int): Width of the rectangle in pixels - height (int): Height of the rectangle in pixels - color (Color): Fill color

    Example:

    renderer.drawFilledRectangle(10, 10, 100, 50, Color::Green);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawlineint-x1-int-y1-int-x2-int-y2-color-color","title":"void drawLine(int x1, int y1, int x2, int y2, Color color)","text":"

    Draws a line between two points.

    Parameters: - x1 (int): Start X coordinate - y1 (int): Start Y coordinate - x2 (int): End X coordinate - y2 (int): End Y coordinate - color (Color): Line color

    Example:

    renderer.drawLine(0, 0, 128, 128, Color::White);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawpixelint-x-int-y-color-color","title":"void drawPixel(int x, int y, Color color)","text":"

    Draws a single pixel.

    Parameters: - x (int): X coordinate - y (int): Y coordinate - color (Color): Pixel color

    Performance Notes: - Very fast, but avoid calling thousands of times per frame - Use for special effects or debugging

    Example:

    renderer.drawPixel(64, 64, Color::Red);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawspriteconst-sprite-sprite-int-x-int-y-color-color-bool-flipx-false","title":"void drawSprite(const Sprite& sprite, int x, int y, Color color, bool flipX = false)","text":"

    Draws a 1bpp monochrome sprite using the Sprite descriptor.

    Parameters: - sprite (const Sprite&): Sprite descriptor (data, width, height) - x (int): Top-left X coordinate in logical screen space - y (int): Top-left Y coordinate in logical screen space - color (Color): Color used for \"on\" pixels. Default: uses sprite palette context - flipX (bool, optional): If true, sprite is mirrored horizontally. Default: false

    Performance Notes: - Very efficient for 1bpp sprites (integer-only operations) - Sprite data should be stored in flash (const/constexpr) for best performance - Avoid calling in tight loops; batch similar operations when possible

    Example:

    static const uint16_t playerData[] = {\n    0b00111100,\n    0b01111110,\n    // ... more rows\n};\n\nstatic const Sprite playerSprite = {\n    playerData,\n    8,  // width\n    8   // height\n};\n\nrenderer.drawSprite(playerSprite, 100, 100, Color::White);\nrenderer.drawSprite(playerSprite, 120, 100, Color::White, true);  // Flipped\n

    "},{"location":"api_reference/graphics/renderer/#void-drawspriteconst-sprite-sprite-int-x-int-y-float-scalex-float-scaley-color-color-bool-flipx-false","title":"void drawSprite(const Sprite& sprite, int x, int y, float scaleX, float scaleY, Color color, bool flipX = false)","text":"

    Draws a scaled 1bpp monochrome sprite.

    Parameters: - sprite (const Sprite&): Sprite descriptor - x (int): Top-left X coordinate - y (int): Top-left Y coordinate - scaleX (float): Horizontal scaling factor (e.g., 1.25 for 25% larger) - scaleY (float): Vertical scaling factor - color (Color): Color used for \"on\" pixels - flipX (bool, optional): If true, sprite is mirrored horizontally before scaling. Default: false

    Performance Notes: - Slower than non-scaled version due to scaling calculations - Use integer scaling factors when possible (1.0, 2.0, etc.) for better performance

    Example:

    renderer.drawSprite(playerSprite, 100, 100, 2.0f, 2.0f, Color::White);  // 2x size\n

    "},{"location":"api_reference/graphics/renderer/#void-drawmultispriteconst-multisprite-sprite-int-x-int-y","title":"void drawMultiSprite(const MultiSprite& sprite, int x, int y)","text":"

    Draws a multi-layer sprite composed of several 1bpp layers.

    Parameters: - sprite (const MultiSprite&): Multi-layer sprite descriptor - x (int): Top-left X coordinate in logical screen space - y (int): Top-left Y coordinate in logical screen space

    Performance Notes: - Each layer is rendered separately, so more layers = more draw calls - Still efficient as each layer uses 1bpp format - Use for multi-color sprites without higher bit-depths

    Example:

    static const SpriteLayer layers[] = {\n    { outlineData, Color::Black },\n    { fillData, Color::Red },\n    { highlightData, Color::Yellow }\n};\n\nstatic const MultiSprite playerMultiSprite = {\n    8,      // width\n    8,      // height\n    layers, // layers array\n    3       // layer count\n};\n\nrenderer.drawMultiSprite(playerMultiSprite, 100, 100);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawmultispriteconst-multisprite-sprite-int-x-int-y-float-scalex-float-scaley","title":"void drawMultiSprite(const MultiSprite& sprite, int x, int y, float scaleX, float scaleY)","text":"

    Draws a scaled multi-layer sprite.

    Parameters: - sprite (const MultiSprite&): Multi-layer sprite descriptor - x (int): Top-left X coordinate - y (int): Top-left Y coordinate - scaleX (float): Horizontal scaling factor - scaleY (float): Vertical scaling factor

    "},{"location":"api_reference/graphics/renderer/#void-drawtilemapconst-tilemap-map-int-originx-int-originy-color-color","title":"void drawTileMap(const TileMap& map, int originX, int originY, Color color)","text":"

    Draws a 1bpp tilemap.

    Parameters: - map (const TileMap&): Tilemap descriptor (indices, 1bpp tiles, dimensions) - originX (int): X coordinate of the top-left corner - originY (int): Y coordinate of the top-left corner - color (Color): Color used for all tiles in the map

    "},{"location":"api_reference/graphics/renderer/#void-drawtilemapconst-tilemap2bpp-map-int-originx-int-originy","title":"void drawTileMap(const TileMap2bpp& map, int originX, int originY)","text":"

    Draws a 2bpp tilemap. Available when PIXELROOT32_ENABLE_2BPP_SPRITES is defined.

    Parameters: - map (const TileMap2bpp&): Tilemap descriptor (indices, 2bpp tiles, dimensions) - originX (int): X coordinate - originY (int): Y coordinate

    "},{"location":"api_reference/graphics/renderer/#void-drawtilemapconst-tilemap4bpp-map-int-originx-int-originy","title":"void drawTileMap(const TileMap4bpp& map, int originX, int originY)","text":"

    Draws a 4bpp tilemap. Available when PIXELROOT32_ENABLE_4BPP_SPRITES is defined.

    Parameters: - map (const TileMap4bpp&): Tilemap descriptor (indices, 4bpp tiles, dimensions) - originX (int): X coordinate - originY (int): Y coordinate

    Performance Notes: - Very efficient for rendering large backgrounds - Only visible tiles are drawn (viewport culling) - Use tilemaps instead of individual sprites for backgrounds

    Example:

    static const uint8_t levelIndices[] = {\n    0, 1, 2, 3,\n    4, 5, 6, 7,\n    // ... more rows\n};\n\nstatic const TileMap levelMap = {\n    levelIndices,\n    16,        // width in tiles\n    16,        // height in tiles\n    tileSprites, // tile sprite array\n    8,         // tile width\n    8,         // tile height\n    16         // tile count\n};\n\nrenderer.drawTileMap(levelMap, 0, 0, Color::White);\n

    "},{"location":"api_reference/graphics/renderer/#void-setdisplayoffsetint-x-int-y","title":"void setDisplayOffset(int x, int y)","text":"

    Sets a global offset for all drawing operations. Useful for camera/parallax effects.

    Parameters: - x (int): X offset in pixels - y (int): Y offset in pixels

    Notes: - All subsequent drawing operations are offset by this amount - Useful for camera scrolling and parallax effects - Reset to (0, 0) to disable offset

    Example:

    // Camera scrolling\ncamera.setPosition(playerX - 64, playerY - 64);\nrenderer.setDisplayOffset(-camera.getX(), -camera.getY());\nrenderer.drawTileMap(background, 0, 0, Color::White);\n

    "},{"location":"api_reference/graphics/renderer/#void-setdisplaysizeint-w-int-h","title":"void setDisplaySize(int w, int h)","text":"

    Sets the logical display size.

    Parameters: - w (int): Width in pixels - h (int): Height in pixels

    Notes: - Typically set via DisplayConfig during construction - Use this to change display size at runtime if needed

    "},{"location":"api_reference/graphics/renderer/#int-getwidth-const","title":"int getWidth() const","text":"

    Gets the display width.

    Returns: - int: Display width in pixels

    "},{"location":"api_reference/graphics/renderer/#int-getheight-const","title":"int getHeight() const","text":"

    Gets the display height.

    Returns: - int: Display height in pixels

    "},{"location":"api_reference/graphics/renderer/#int-getxoffset-const","title":"int getXOffset() const","text":"

    Gets the current X display offset.

    Returns: - int: X offset in pixels

    "},{"location":"api_reference/graphics/renderer/#int-getyoffset-const","title":"int getYOffset() const","text":"

    Gets the current Y display offset.

    Returns: - int: Y offset in pixels

    "},{"location":"api_reference/graphics/renderer/#void-setcontrastuint8_t-level","title":"void setContrast(uint8_t level)","text":"

    Sets the display contrast (brightness).

    Parameters: - level (uint8_t): Contrast level (0-255)

    Notes: - Platform-specific: may not be supported on all displays - Higher values = brighter display

    "},{"location":"api_reference/graphics/renderer/#void-setfontconst-uint8_t-font","title":"void setFont(const uint8_t* font)","text":"

    Sets the font for text rendering.

    Parameters: - font (const uint8_t*): Pointer to the font data

    Notes: - Sets the default font for drawText() calls without font parameter - Use font constants like FONT_5X7 from Font.h

    "},{"location":"api_reference/graphics/renderer/#usage-example","title":"Usage Example","text":"
    #include \"graphics/Renderer.h\"\n#include \"graphics/DisplayConfig.h\"\n\nvoid draw(Renderer& renderer) override {\n    renderer.beginFrame();\n\n    // Draw background\n    renderer.drawFilledRectangle(0, 0, 128, 128, Color::Black);\n\n    // Draw sprites\n    renderer.drawSprite(playerSprite, playerX, playerY, Color::White);\n    renderer.drawSprite(enemySprite, enemyX, enemyY, Color::Red);\n\n    // Draw UI\n    renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n    renderer.drawTextCentered(\"Game Over\", 64, Color::Yellow, 2);\n\n    renderer.endFrame();\n}\n
    "},{"location":"api_reference/graphics/renderer/#performance-considerations","title":"Performance Considerations","text":"
    • Integer-only math: All operations use integer arithmetic for ESP32 efficiency
    • Sprite storage: Store sprite data in flash (const/constexpr) for best performance
    • Batch operations: Group similar draw calls together
    • Tilemaps: Dibuja un mapa de tiles completo. Implementa viewport culling autom\u00e1tico y cach\u00e9 de paleta para m\u00e1ximo rendimiento.
    • Sprites 2bpp/4bpp: Optimizado para ESP32 (IRAM + acceso de 16 bits).
    "},{"location":"api_reference/graphics/renderer/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Sprite data should be in flash, not RAM
    • Frame rate: Limit draw calls per frame for consistent FPS
    • Display offset: Use for scrolling instead of redrawing everything
    "},{"location":"api_reference/graphics/renderer/#see-also","title":"See Also","text":"
    • Sprite - Sprite structure
    • MultiSprite - Multi-layer sprites
    • TileMap - Tilemap structure
    • Color - Color constants
    • DisplayConfig - Display configuration
    • Camera2D - Camera for scrolling
    • Manual - Basic Rendering
    • Manual - Sprites and Animation
    • API Overview
    "},{"location":"api_reference/graphics/sprite/","title":"Sprite","text":"

    Low-level bitmap descriptor and multi-layer composition for retro rendering.

    "},{"location":"api_reference/graphics/sprite/#description","title":"Description","text":"

    Sprites are the fundamental graphics primitive in PixelRoot32. The engine supports multiple sprite formats:

    • 1bpp (Standard): Monochrome sprites, most memory-efficient
    • 2bpp (Experimental): 4 colors per sprite
    • 4bpp (Experimental): 16 colors per sprite
    • MultiSprite: Multi-layer 1bpp sprites for multi-color effects
    "},{"location":"api_reference/graphics/sprite/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    struct Sprite {\n        // ...\n    };\n\n    struct MultiSprite {\n        // ...\n    };\n\n    struct SpriteLayer {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/sprite/#sprite-structure-1bpp","title":"Sprite Structure (1bpp)","text":"

    Compact sprite descriptor for monochrome bitmapped sprites.

    "},{"location":"api_reference/graphics/sprite/#members","title":"Members","text":"
    • const uint16_t* data: Pointer to packed row data (size = height)
    • uint8_t width: Sprite width in pixels (\u2264 16)
    • uint8_t height: Sprite height in pixels
    "},{"location":"api_reference/graphics/sprite/#data-format","title":"Data Format","text":"

    Sprites are stored as an array of 16-bit rows. Each row packs horizontal pixels into bits:

    • Bit 0: Leftmost pixel of the row
    • Bit (width - 1): Rightmost pixel of the row
    • Bit value 1: Pixel on (colored)
    • Bit value 0: Pixel off (transparent/background)

    Only the lowest width bits of each row are used.

    "},{"location":"api_reference/graphics/sprite/#example","title":"Example","text":"
    // 8x8 sprite (smiley face)\nstatic const uint16_t SMILEY_DATA[] = {\n    0b00111100,  // Row 0:  \u2588\u2588\u2588\u2588\n    0b01111110,  // Row 1:  \u2588\u2588\u2588\u2588\u2588\u2588\n    0b11011011,  // Row 2:  \u2588\u2588 \u2588\u2588 \u2588\u2588\n    0b11111111,  // Row 3:  \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\n    0b11011011,  // Row 4:  \u2588\u2588 \u2588\u2588 \u2588\u2588\n    0b01100110,  // Row 5:  \u2588\u2588  \u2588\u2588\n    0b01111110,  // Row 6:  \u2588\u2588\u2588\u2588\u2588\u2588\n    0b00111100   // Row 7:  \u2588\u2588\u2588\u2588\n};\n\nstatic const Sprite SMILEY_SPRITE = {\n    SMILEY_DATA,\n    8,  // width\n    8   // height\n};\n
    "},{"location":"api_reference/graphics/sprite/#spritelayer-structure","title":"SpriteLayer Structure","text":"

    Single monochrome layer used by layered sprites.

    "},{"location":"api_reference/graphics/sprite/#members_1","title":"Members","text":"
    • const uint16_t* data: Pointer to packed row data for this layer
    • Color color: Color used for \"on\" pixels in this layer
    "},{"location":"api_reference/graphics/sprite/#notes","title":"Notes","text":"
    • Each layer uses the same width/height as its owning MultiSprite
    • Layers can have different colors
    • Layers are drawn in array order
    "},{"location":"api_reference/graphics/sprite/#multisprite-structure","title":"MultiSprite Structure","text":"

    Multi-layer, multi-color sprite built from 1bpp layers.

    "},{"location":"api_reference/graphics/sprite/#members_2","title":"Members","text":"
    • uint8_t width: Sprite width in pixels (\u2264 16)
    • uint8_t height: Sprite height in pixels
    • const SpriteLayer* layers: Pointer to array of layers
    • uint8_t layerCount: Number of layers in the array
    "},{"location":"api_reference/graphics/sprite/#notes_1","title":"Notes","text":"
    • Combines several 1bpp layers with different colors
    • Layers are drawn in array order
    • Enables multi-color sprites without higher bit-depths
    • More layers = more draw calls (but still efficient)
    "},{"location":"api_reference/graphics/sprite/#example_1","title":"Example","text":"
    // Outline layer\nstatic const uint16_t OUTLINE_DATA[] = {\n    0b11111111,\n    0b10000001,\n    0b10000001,\n    0b11111111\n};\n\n// Fill layer\nstatic const uint16_t FILL_DATA[] = {\n    0b00000000,\n    0b01111110,\n    0b01111110,\n    0b00000000\n};\n\nstatic const SpriteLayer LAYERS[] = {\n    {OUTLINE_DATA, Color::Black},  // Layer 0: Black outline\n    {FILL_DATA, Color::Red}       // Layer 1: Red fill\n};\n\nstatic const MultiSprite PLAYER_MULTISPRITE = {\n    8,      // width\n    8,      // height\n    LAYERS, // layers array\n    2       // layer count\n};\n
    "},{"location":"api_reference/graphics/sprite/#sprite2bpp-structure-experimental","title":"Sprite2bpp Structure (Experimental)","text":"

    2-bit per pixel sprite (4 colors).

    Requires: PIXELROOT32_ENABLE_2BPP_SPRITES build flag

    "},{"location":"api_reference/graphics/sprite/#members_3","title":"Members","text":"
    • const uint16_t* data: Datos empaquetados (4 p\u00edxeles por cada 8 bits, alineados a 16 bits)
    • const Color* palette: Local palette (4 colors)
    • uint8_t width: Sprite width
    • uint8_t height: Sprite height
    • uint8_t paletteSize: Number of colors (typically 4)
    "},{"location":"api_reference/graphics/sprite/#notes_2","title":"Notes","text":"
    • Experimental feature
    • Uses more memory than 1bpp
    • Each pixel can be one of 4 colors from local palette
    "},{"location":"api_reference/graphics/sprite/#sprite4bpp-structure-experimental","title":"Sprite4bpp Structure (Experimental)","text":"

    4-bit per pixel sprite (16 colors).

    Requires: PIXELROOT32_ENABLE_4BPP_SPRITES build flag

    "},{"location":"api_reference/graphics/sprite/#members_4","title":"Members","text":"
    • const uint16_t* data: Datos empaquetados (2 p\u00edxeles por cada 8 bits, alineados a 16 bits)
    • const Color* palette: Local palette (16 colors)
    • uint8_t width: Sprite width
    • uint8_t height: Sprite height
    • uint8_t paletteSize: Number of colors (typically 16)
    "},{"location":"api_reference/graphics/sprite/#notes_3","title":"Notes","text":"
    • Experimental feature
    • Uses more memory than 1bpp/2bpp
    • Each pixel can be one of 16 colors from local palette
    "},{"location":"api_reference/graphics/sprite/#sprite-animation","title":"Sprite Animation","text":""},{"location":"api_reference/graphics/sprite/#spriteanimationframe-structure","title":"SpriteAnimationFrame Structure","text":"

    Frame that can reference either a Sprite or a MultiSprite.

    Members: - const Sprite* sprite: Optional pointer to a simple 1bpp sprite frame - const MultiSprite* multiSprite: Optional pointer to a layered sprite frame

    Notes: - Exactly one pointer should be non-null for a valid frame - Allows same animation system for both sprite types

    "},{"location":"api_reference/graphics/sprite/#spriteanimation-structure","title":"SpriteAnimation Structure","text":"

    Lightweight, step-based sprite animation controller.

    Members: - const SpriteAnimationFrame* frames: Pointer to immutable frame table - uint8_t frameCount: Number of frames in the table - uint8_t current: Current frame index [0, frameCount)

    Methods: - void reset(): Reset to first frame - void step(): Advance to next frame (wrapping) - const SpriteAnimationFrame& getCurrentFrame() const: Get current frame - const Sprite* getCurrentSprite() const: Get current simple sprite - const MultiSprite* getCurrentMultiSprite() const: Get current multi-sprite

    Example:

    static const SpriteAnimationFrame WALK_FRAMES[] = {\n    {&walkFrame1, nullptr},\n    {&walkFrame2, nullptr},\n    {&walkFrame3, nullptr},\n    {&walkFrame2, nullptr}  // Loop back\n};\n\nstatic SpriteAnimation walkAnimation = {\n    WALK_FRAMES,\n    4,    // frameCount\n    0     // current\n};\n\n// In update\nwalkAnimation.step();\nconst Sprite* currentSprite = walkAnimation.getCurrentSprite();\n

    "},{"location":"api_reference/graphics/sprite/#usage-examples","title":"Usage Examples","text":""},{"location":"api_reference/graphics/sprite/#creating-1bpp-sprites","title":"Creating 1bpp Sprites","text":"
    // 8x8 player sprite\nstatic const uint16_t PLAYER_DATA[] = {\n    0b00111100,\n    0b01111110,\n    0b11111111,\n    0b11011011,\n    0b11111111,\n    0b01111110,\n    0b00111100,\n    0b00011000\n};\n\nstatic const Sprite PLAYER_SPRITE = {\n    PLAYER_DATA,\n    8,  // width\n    8   // height\n};\n\n// Use in rendering\nrenderer.drawSprite(PLAYER_SPRITE, 100, 100, Color::White);\n
    "},{"location":"api_reference/graphics/sprite/#creating-multisprite","title":"Creating MultiSprite","text":"
    // Outline layer\nstatic const uint16_t OUTLINE[] = {\n    0b11111111,\n    0b10000001,\n    0b10000001,\n    0b11111111\n};\n\n// Fill layer\nstatic const uint16_t FILL[] = {\n    0b00000000,\n    0b01111110,\n    0b01111110,\n    0b00000000\n};\n\nstatic const SpriteLayer LAYERS[] = {\n    {OUTLINE, Color::Black},\n    {FILL, Color::Red}\n};\n\nstatic const MultiSprite ENEMY_SPRITE = {\n    8,      // width\n    8,      // height\n    LAYERS, // layers\n    2       // layer count\n};\n\n// Use in rendering\nrenderer.drawMultiSprite(ENEMY_SPRITE, 100, 100);\n
    "},{"location":"api_reference/graphics/sprite/#sprite-animation_1","title":"Sprite Animation","text":"
    class AnimatedActor : public pixelroot32::core::Actor {\nprivate:\n    SpriteAnimation animation;\n    unsigned long animTimer = 0;\n    unsigned long animInterval = 200;  // 200ms per frame\n\npublic:\n    void update(unsigned long deltaTime) override {\n        Actor::update(deltaTime);\n\n        animTimer += deltaTime;\n        if (animTimer >= animInterval) {\n            animTimer -= animInterval;\n            animation.step();\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        const Sprite* frame = animation.getCurrentSprite();\n        if (frame) {\n            renderer.drawSprite(*frame, \n                               static_cast<int>(x), \n                               static_cast<int>(y), \n                               Color::White);\n        }\n    }\n};\n
    "},{"location":"api_reference/graphics/sprite/#sprite-flipping","title":"Sprite Flipping","text":"

    Sprites can be flipped horizontally:

    // Draw normal\nrenderer.drawSprite(sprite, 100, 100, Color::White, false);\n\n// Draw flipped\nrenderer.drawSprite(sprite, 100, 100, Color::White, true);\n
    "},{"location":"api_reference/graphics/sprite/#performance-considerations","title":"Performance Considerations","text":"
    • 1bpp sprites: Most efficient (integer-only operations)
    • MultiSprite: Each layer is a separate draw call (still efficient)
    • 2bpp/4bpp: Experimental, uses more memory and CPU
    • Storage: Store sprite data in flash (const/constexpr) for best performance
    • Size limit: Sprites are limited to 16 pixels wide for 1bpp format
    "},{"location":"api_reference/graphics/sprite/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Store sprite data in flash, not RAM
    • Sprite size: Smaller sprites = faster drawing
    • Format choice: Use 1bpp when possible for best performance
    • MultiSprite: More layers = more draw calls (but acceptable)
    "},{"location":"api_reference/graphics/sprite/#see-also","title":"See Also","text":"
    • Renderer - Rendering system that draws sprites
    • Color - Color constants for sprites
    • Manual - Sprites and Animation
    • API Overview
    "},{"location":"api_reference/graphics/tilemap/","title":"TileMap","text":"

    Generic structure for tile-based background rendering.

    "},{"location":"api_reference/graphics/tilemap/#description","title":"Description","text":"

    TileMapGeneric<T> is a template structure for rendering tile-based backgrounds efficiently. It supports multiple bit-depths (1bpp, 2bpp, 4bpp) by using the appropriate sprite type for tiles.

    Tilemaps are ideal for large backgrounds, levels, and static environments. They support viewport culling (only visible tiles are drawn) for optimal performance.

    "},{"location":"api_reference/graphics/tilemap/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    template<typename T>\n    struct TileMapGeneric {\n        // ...\n    };\n\n    using TileMap = TileMapGeneric<Sprite>;\n    using TileMap2bpp = TileMapGeneric<Sprite2bpp>;\n    using TileMap4bpp = TileMapGeneric<Sprite4bpp>;\n}\n
    "},{"location":"api_reference/graphics/tilemap/#template-parameters","title":"Template Parameters","text":""},{"location":"api_reference/graphics/tilemap/#t","title":"T","text":"

    The sprite type used for tiles.

    Supported types: - Sprite (1bpp) - Sprite2bpp (2bpp) - Sprite4bpp (4bpp)

    "},{"location":"api_reference/graphics/tilemap/#structure","title":"Structure","text":""},{"location":"api_reference/graphics/tilemap/#uint8_t-indices","title":"uint8_t* indices","text":"

    Array of tile indices mapping to tile sprites.

    Type: uint8_t*

    Access: Read-write

    Notes: - Array size = width * height - Each value is an index into the tiles array - 0 = first tile, 1 = second tile, etc. - Should be stored in flash (const) for best performance

    Example:

    // 16x16 tilemap (256 tiles)\nstatic const uint8_t LEVEL_INDICES[] = {\n    0, 0, 0, 0, 1, 1, 1, 1, // Row 0\n    0, 2, 2, 2, 2, 2, 2, 0, // Row 1\n    // ... more rows\n};\n

    "},{"location":"api_reference/graphics/tilemap/#uint8_t-width","title":"uint8_t width","text":"

    Width of the tilemap in tiles.

    Type: uint8_t

    Access: Read-write

    Example:

    width = 16;  // 16 tiles wide\n

    "},{"location":"api_reference/graphics/tilemap/#uint8_t-height","title":"uint8_t height","text":"

    Height of the tilemap in tiles.

    Type: uint8_t

    Access: Read-write

    Example:

    height = 16;  // 16 tiles tall\n

    "},{"location":"api_reference/graphics/tilemap/#const-t-tiles","title":"const T* tiles","text":"

    Array of tile sprites of type T.

    Type: const T*

    Access: Read-only

    Notes: - Array of sprite pointers, one per unique tile - Indices reference this array - All tiles should be the same size - Should be stored in flash (const) for best performance

    Example (1bpp):

    static const Sprite TILE_SPRITES[] = {\n    EMPTY_TILE,   // Index 0\n    WALL_TILE,    // Index 1\n    FLOOR_TILE,   // Index 2\n    // ... more tiles\n};\n

    Example (2bpp):

    static const Sprite2bpp TILE_SPRITES_2BPP[] = {\n    TILE_GRASS,   // Index 0\n    TILE_DIRT,    // Index 1\n    // ... more tiles\n};\n

    "},{"location":"api_reference/graphics/tilemap/#uint8_t-tilewidth","title":"uint8_t tileWidth","text":"

    Width of each tile in pixels.

    Type: uint8_t

    Access: Read-write

    Notes: - All tiles must have the same width - Common values: 8, 16 pixels - Should match sprite width

    Example:

    tileWidth = 8;  // 8x8 tiles\n

    "},{"location":"api_reference/graphics/tilemap/#uint8_t-tileheight","title":"uint8_t tileHeight","text":"

    Height of each tile in pixels.

    Type: uint8_t

    Access: Read-write

    Notes: - All tiles must have the same height - Common values: 8, 16 pixels - Should match sprite height

    Example:

    tileHeight = 8;  // 8x8 tiles\n

    "},{"location":"api_reference/graphics/tilemap/#uint16_t-tilecount","title":"uint16_t tileCount","text":"

    Number of unique tiles in the tiles array.

    Type: uint16_t

    Access: Read-write

    Notes: - Must match the size of the tiles array - Indices must be < tileCount

    Example:

    tileCount = 16;  // 16 unique tiles\n

    "},{"location":"api_reference/graphics/tilemap/#creating-tilemaps","title":"Creating Tilemaps","text":""},{"location":"api_reference/graphics/tilemap/#step-1-create-tile-sprites","title":"Step 1: Create Tile Sprites","text":"
    // Empty tile (index 0)\nstatic const uint16_t EMPTY_DATA[] = {\n    0b00000000,\n    0b00000000,\n    0b00000000,\n    0b00000000,\n    0b00000000,\n    0b00000000,\n    0b00000000,\n    0b00000000\n};\n\n// Wall tile (index 1)\nstatic const uint16_t WALL_DATA[] = {\n    0b11111111,\n    0b10000001,\n    0b10000001,\n    0b10000001,\n    0b10000001,\n    0b10000001,\n    0b10000001,\n    0b11111111\n};\n\n// Floor tile (index 2)\nstatic const uint16_t FLOOR_DATA[] = {\n    0b00000000,\n    0b01111110,\n    0b01111110,\n    0b01111110,\n    0b01111110,\n    0b01111110,\n    0b01111110,\n    0b00000000\n};\n\nstatic const Sprite TILE_SPRITES[] = {\n    {EMPTY_DATA, 8, 8},  // Index 0\n    {WALL_DATA, 8, 8},   // Index 1\n    {FLOOR_DATA, 8, 8}   // Index 2\n};\n
    "},{"location":"api_reference/graphics/tilemap/#step-2-create-index-array","title":"Step 2: Create Index Array","text":"
    // 16x16 level (256 tiles)\n// 0 = empty, 1 = wall, 2 = floor\nstatic const uint8_t LEVEL_INDICES[] = {\n    // Row 0: Top wall\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n    // Row 1: Walls on sides\n    1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,\n    // Row 2\n    1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,\n    // ... more rows\n    // Row 15: Bottom wall\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1\n};\n
    "},{"location":"api_reference/graphics/tilemap/#step-3-create-tilemap-structure","title":"Step 3: Create TileMap Structure","text":"
    static const TileMap LEVEL_MAP = {\n    const_cast<uint8_t*>(LEVEL_INDICES),  // indices (non-const for struct)\n    16,        // width in tiles\n    16,        // height in tiles\n    TILE_SPRITES, // tile sprites array\n    8,         // tile width\n    8,         // tile height\n    3          // tile count\n};\n
    "},{"location":"api_reference/graphics/tilemap/#rendering-tilemaps","title":"Rendering Tilemaps","text":"

    Use Renderer::drawTileMap():

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Draw tilemap at origin (0, 0)\n    renderer.drawTileMap(LEVEL_MAP, 0, 0, Color::White);\n\n    // With camera offset\n    camera.apply(renderer);\n    renderer.drawTileMap(LEVEL_MAP, 0, 0, Color::White);\n}\n
    "},{"location":"api_reference/graphics/tilemap/#viewport-culling","title":"Viewport Culling","text":"

    Tilemaps automatically cull tiles outside the viewport:

    • Only visible tiles are drawn
    • Very efficient for large levels
    • Works with camera scrolling

    Example:

    // Large level (256x256 tiles)\n// Only tiles visible on screen are drawn\ncamera.apply(renderer);\nrenderer.drawTileMap(LARGE_LEVEL_MAP, 0, 0, Color::White);\n

    "},{"location":"api_reference/graphics/tilemap/#collision-detection-with-tilemaps","title":"Collision Detection with Tilemaps","text":"

    Check tile at world position:

    bool isSolidTile(int worldX, int worldY, const TileMap& map) {\n    int tileX = worldX / map.tileWidth;\n    int tileY = worldY / map.tileHeight;\n\n    if (tileX < 0 || tileX >= map.width || \n        tileY < 0 || tileY >= map.height) {\n        return true;  // Outside map = solid\n    }\n\n    int index = tileY * map.width + tileX;\n    uint8_t tileIndex = map.indices[index];\n\n    // Check if tile is solid (e.g., wall = index 1)\n    return (tileIndex == 1);\n}\n
    "},{"location":"api_reference/graphics/tilemap/#usage-example","title":"Usage Example","text":"
    #include \"graphics/TileMap.h\"\n#include \"graphics/Renderer.h\"\n\nclass LevelScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::TileMap levelMap;\n    pixelroot32::graphics::Camera2D camera;\n\npublic:\n    void init() override {\n        // Level map is already defined (see above)\n        // Create camera\n        auto& renderer = engine.getRenderer();\n        camera = pixelroot32::graphics::Camera2D(\n            renderer.getWidth(),\n            renderer.getHeight()\n        );\n\n        // Set level boundaries\n        int levelWidth = levelMap.width * levelMap.tileWidth;\n        int levelHeight = levelMap.height * levelMap.tileHeight;\n        camera.setBounds(0.0f, levelWidth - renderer.getWidth());\n        camera.setVerticalBounds(0.0f, levelHeight - renderer.getHeight());\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Camera follows player\n        if (player) {\n            camera.followTarget(player->x, player->y);\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Apply camera\n        camera.apply(renderer);\n\n        // Draw tilemap (viewport culling automatic)\n        renderer.drawTileMap(levelMap, 0, 0, Color::White);\n\n        // Draw entities\n        Scene::draw(renderer);\n\n        // Reset for UI\n        renderer.setDisplayOffset(0, 0);\n    }\n\n    bool checkTileCollision(float x, float y) {\n        int tileX = static_cast<int>(x) / levelMap.tileWidth;\n        int tileY = static_cast<int>(y) / levelMap.tileHeight;\n\n        if (tileX < 0 || tileX >= levelMap.width || \n            tileY < 0 || tileY >= levelMap.height) {\n            return true;  // Outside = solid\n        }\n\n        int index = tileY * levelMap.width + tileX;\n        uint8_t tile = levelMap.indices[index];\n        return (tile == 1);  // Wall tile\n    }\n};\n
    "},{"location":"api_reference/graphics/tilemap/#performance-considerations","title":"Performance Considerations","text":"
    • Viewport culling: Only visible tiles are drawn (automatic)
    • Tile reuse: Reuse tile sprites across the map
    • Index storage: Compact uint8_t indices (1 byte per tile)
    • Memory: Store indices and tiles in flash (const) for best performance
    • Tile size: Smaller tiles = more tiles to draw, but more detail
    "},{"location":"api_reference/graphics/tilemap/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Store tilemap data in flash, not RAM
    • Map size: Large maps use more flash memory
    • Tile count: Limit unique tiles to save memory
    • Culling: Viewport culling is essential for large levels
    "},{"location":"api_reference/graphics/tilemap/#see-also","title":"See Also","text":"
    • Renderer - Rendering system that draws tilemaps
    • Sprite - Sprite structure used for tiles
    • Camera2D - Camera for scrolling tilemaps
    • Manual - Tilemaps
    • API Overview
    "},{"location":"api_reference/physics/collision_system/","title":"CollisionSystem","text":"

    Manages collision detection between entities.

    "},{"location":"api_reference/physics/collision_system/#description","title":"Description","text":"

    CollisionSystem iterates through registered entities, checks if they are Actors, and performs AABB (Axis-Aligned Bounding Box) collision checks based on their collision layers and masks.

    The system automatically filters collisions using layers and masks, avoiding unnecessary checks. When a collision is detected, it triggers the onCollision() callback on both actors.

    "},{"location":"api_reference/physics/collision_system/#namespace","title":"Namespace","text":"
    namespace pixelroot32::physics {\n    class CollisionSystem {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/physics/collision_system/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Scene (manages collision system instance)
    "},{"location":"api_reference/physics/collision_system/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/physics/collision_system/#void-addentityentity-e","title":"void addEntity(Entity* e)","text":"

    Adds an entity to the collision system.

    Parameters: - e (pixelroot32::core::Entity*): Pointer to the entity to add

    Returns: - void

    Notes: - Only Actor entities participate in collisions - Entities are automatically added when added to Scene - Typically not called directly (handled by Scene)

    Example:

    // Typically handled automatically by Scene\nscene->addEntity(actor);  // Automatically added to collision system\n

    "},{"location":"api_reference/physics/collision_system/#void-removeentityentity-e","title":"void removeEntity(Entity* e)","text":"

    Removes an entity from the collision system.

    Parameters: - e (pixelroot32::core::Entity*): Pointer to the entity to remove

    Returns: - void

    Notes: - Entities are automatically removed when removed from Scene - Typically not called directly

    Example:

    // Typically handled automatically by Scene\nscene->removeEntity(actor);  // Automatically removed from collision system\n

    "},{"location":"api_reference/physics/collision_system/#void-update","title":"void update()","text":"

    Performs collision detection for all registered entities.

    Returns: - void

    Notes: - Called automatically by Scene::update() - Iterates through all pairs of entities - Only checks collisions between Actors with matching layers/masks - Triggers onCollision() callbacks when collisions are detected - Uses AABB (Axis-Aligned Bounding Box) collision detection

    Example:

    // Called automatically by Scene, but can be called manually:\nvoid update(unsigned long deltaTime) override {\n    Scene::update(deltaTime);  // Calls collisionSystem.update()\n\n    // Or manually:\n    collisionSystem.update();\n}\n

    "},{"location":"api_reference/physics/collision_system/#how-it-works","title":"How It Works","text":"
    1. Entity Registration: Entities added to Scene are automatically registered
    2. Layer Filtering: Only actors with matching layers/masks are checked
    3. AABB Check: Uses Rect::intersects() for collision detection
    4. Callback: Calls onCollision() on both actors when collision detected
    "},{"location":"api_reference/physics/collision_system/#collision-detection-algorithm","title":"Collision Detection Algorithm","text":"
    for each actor1 in entities:\n    if actor1 is not an Actor: continue\n    for each actor2 in entities:\n        if actor2 is not an Actor: continue\n        if actor1 == actor2: continue\n\n        // Check layers/masks\n        if (actor1->layer & actor2->mask) == 0: continue\n        if (actor2->layer & actor1->mask) == 0: continue\n\n        // Check AABB intersection\n        Rect box1 = actor1->getHitBox();\n        Rect box2 = actor2->getHitBox();\n\n        if (box1.intersects(box2)) {\n            actor1->onCollision(actor2);\n            actor2->onCollision(actor1);\n        }\n
    "},{"location":"api_reference/physics/collision_system/#usage-example","title":"Usage Example","text":"
    #include \"physics/CollisionSystem.h\"\n#include \"core/Actor.h\"\n\nclass GameScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Create actors with collision layers\n        PlayerActor* player = new PlayerActor(64, 64);\n        player->layer = pixelroot32::physics::DefaultLayers::kPlayer;\n        player->mask = pixelroot32::physics::DefaultLayers::kEnemy | \n                       pixelroot32::physics::DefaultLayers::kObstacle;\n        addEntity(player);\n\n        EnemyActor* enemy = new EnemyActor(100, 100);\n        enemy->layer = pixelroot32::physics::DefaultLayers::kEnemy;\n        enemy->mask = pixelroot32::physics::DefaultLayers::kPlayer;\n        addEntity(enemy);\n\n        // Collision system is managed by Scene\n        // Collisions are checked automatically in Scene::update()\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Collision detection happens here automatically\n        Scene::update(deltaTime);\n    }\n};\n
    "},{"location":"api_reference/physics/collision_system/#performance-considerations","title":"Performance Considerations","text":"
    • Layer filtering: Very efficient; avoids most collision checks
    • AABB checks: Fast (simple rectangle intersection)
    • Pair checking: O(n\u00b2) complexity, but n is limited (MAX_ENTITIES = 32)
    • Update frequency: Called every frame; keep hitboxes simple
    "},{"location":"api_reference/physics/collision_system/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Entity limit: MAX_ENTITIES = 32 limits collision pairs
    • Layer efficiency: Use layers effectively to minimize checks
    • Hitbox simplicity: Keep hitboxes as simple AABB for best performance
    "},{"location":"api_reference/physics/collision_system/#see-also","title":"See Also","text":"
    • Actor - Actors that participate in collisions
    • CollisionTypes - Collision primitives and layers
    • Manual - Physics and Collisions
    • API Overview
    "},{"location":"api_reference/physics/collision_types/","title":"Collision Types","text":"

    Basic geometric types and collision layer definitions.

    "},{"location":"api_reference/physics/collision_types/#description","title":"Description","text":"

    This document describes the collision primitives (Rect, Circle, Segment) and collision layer system used by the collision detection system.

    "},{"location":"api_reference/physics/collision_types/#namespace","title":"Namespace","text":"
    namespace pixelroot32::physics {\n    // Types and functions\n}\n
    "},{"location":"api_reference/physics/collision_types/#collisionlayer-type","title":"CollisionLayer Type","text":"

    Collision layer type (bit flags).

    Type: uint16_t (typedef)

    Notes: - Uses bit flags for layer assignment - Actors can be on multiple layers (bitwise OR) - Masks define which layers an actor can collide with

    "},{"location":"api_reference/physics/collision_types/#defaultlayers-namespace","title":"DefaultLayers Namespace","text":"

    Predefined collision layers.

    "},{"location":"api_reference/physics/collision_types/#knone","title":"kNone","text":"

    No layer (0).

    Value: 0

    Usage:

    actor->layer = pixelroot32::physics::DefaultLayers::kNone;\n

    "},{"location":"api_reference/physics/collision_types/#kall","title":"kAll","text":"

    All layers (0xFFFF).

    Value: 0xFFFF

    Usage:

    actor->mask = pixelroot32::physics::DefaultLayers::kAll;  // Collide with everything\n

    "},{"location":"api_reference/physics/collision_types/#custom-layers","title":"Custom Layers","text":"

    Define custom layers using bit flags:

    namespace MyLayers {\n    const pixelroot32::physics::CollisionLayer kPlayer = 1 << 0;      // Bit 0\n    const pixelroot32::physics::CollisionLayer kEnemy = 1 << 1;       // Bit 1\n    const pixelroot32::physics::CollisionLayer kObstacle = 1 << 2;    // Bit 2\n    const pixelroot32::physics::CollisionLayer kProjectile = 1 << 3;  // Bit 3\n    const pixelroot32::physics::CollisionLayer kCollectible = 1 << 4;  // Bit 4\n}\n\n// Usage\nactor->layer = MyLayers::kPlayer;\nactor->mask = MyLayers::kEnemy | MyLayers::kObstacle;\n
    "},{"location":"api_reference/physics/collision_types/#circle-structure","title":"Circle Structure","text":"

    Represents a circle for collision detection.

    Members: - float x: Center X coordinate - float y: Center Y coordinate - float radius: Radius of the circle

    Example:

    pixelroot32::physics::Circle circle;\ncircle.x = 100.0f;\ncircle.y = 100.0f;\ncircle.radius = 10.0f;\n

    "},{"location":"api_reference/physics/collision_types/#segment-structure","title":"Segment Structure","text":"

    Represents a line segment for collision detection.

    Members: - float x1, y1: Start point coordinates - float x2, y2: End point coordinates

    Example:

    pixelroot32::physics::Segment segment;\nsegment.x1 = 0.0f;\nsegment.y1 = 0.0f;\nsegment.x2 = 100.0f;\nsegment.y2 = 100.0f;\n

    "},{"location":"api_reference/physics/collision_types/#intersection-functions","title":"Intersection Functions","text":""},{"location":"api_reference/physics/collision_types/#bool-intersectsconst-circle-a-const-circle-b","title":"bool intersects(const Circle& a, const Circle& b)","text":"

    Checks if two circles intersect.

    Parameters: - a (const Circle&): First circle - b (const Circle&): Second circle

    Returns: - bool: true if circles intersect

    Example:

    pixelroot32::physics::Circle circle1{100.0f, 100.0f, 10.0f};\npixelroot32::physics::Circle circle2{110.0f, 100.0f, 10.0f};\n\nif (pixelroot32::physics::intersects(circle1, circle2)) {\n    // Circles overlap\n}\n

    "},{"location":"api_reference/physics/collision_types/#bool-intersectsconst-circle-c-const-rect-r","title":"bool intersects(const Circle& c, const Rect& r)","text":"

    Checks if a circle and rectangle intersect.

    Parameters: - c (const Circle&): Circle - r (const Rect&): Rectangle

    Returns: - bool: true if circle and rectangle intersect

    Example:

    pixelroot32::physics::Circle circle{100.0f, 100.0f, 10.0f};\npixelroot32::core::Rect rect{95.0f, 95.0f, 20, 20};\n\nif (pixelroot32::physics::intersects(circle, rect)) {\n    // Circle overlaps rectangle\n}\n

    "},{"location":"api_reference/physics/collision_types/#bool-intersectsconst-segment-s-const-rect-r","title":"bool intersects(const Segment& s, const Rect& r)","text":"

    Checks if a line segment and rectangle intersect.

    Parameters: - s (const Segment&): Line segment - r (const Rect&): Rectangle

    Returns: - bool: true if segment and rectangle intersect

    Example:

    pixelroot32::physics::Segment segment{0.0f, 0.0f, 100.0f, 100.0f};\npixelroot32::core::Rect rect{50.0f, 50.0f, 20, 20};\n\nif (pixelroot32::physics::intersects(segment, rect)) {\n    // Segment intersects rectangle\n}\n

    "},{"location":"api_reference/physics/collision_types/#bool-sweepcirclevsrectconst-circle-start-const-circle-end-const-rect-target-float-thit","title":"bool sweepCircleVsRect(const Circle& start, const Circle& end, const Rect& target, float& tHit)","text":"

    Performs a sweep test: checks if a moving circle collides with a rectangle.

    Parameters: - start (const Circle&): Circle at start position - end (const Circle&): Circle at end position - target (const Rect&): Target rectangle - tHit (float&): Output parameter for collision time (0.0 to 1.0)

    Returns: - bool: true if collision occurs during sweep

    Notes: - Useful for fast-moving projectiles - tHit indicates when collision occurs (0.0 = start, 1.0 = end) - More expensive than simple AABB check

    Example:

    // Projectile moving from (0, 0) to (100, 100)\npixelroot32::physics::Circle start{0.0f, 0.0f, 5.0f};\npixelroot32::physics::Circle end{100.0f, 100.0f, 5.0f};\npixelroot32::core::Rect wall{50.0f, 50.0f, 20, 20};\n\nfloat tHit = 0.0f;\nif (pixelroot32::physics::sweepCircleVsRect(start, end, wall, tHit)) {\n    // Collision at time tHit\n    float hitX = 0.0f + (100.0f - 0.0f) * tHit;\n    float hitY = 0.0f + (100.0f - 0.0f) * tHit;\n}\n

    "},{"location":"api_reference/physics/collision_types/#rect-structure-from-coreentityh","title":"Rect Structure (from core/Entity.h)","text":"

    Represents a 2D rectangle for collision detection.

    Members: - float x, y: Top-left corner coordinates - int width, height: Dimensions

    Methods: - bool intersects(const Rect& other) const: Checks if rectangles overlap

    Example:

    pixelroot32::core::Rect rect1{10.0f, 20.0f, 50, 50};\npixelroot32::core::Rect rect2{30.0f, 40.0f, 50, 50};\n\nif (rect1.intersects(rect2)) {\n    // Rectangles overlap\n}\n

    "},{"location":"api_reference/physics/collision_types/#usage-examples","title":"Usage Examples","text":""},{"location":"api_reference/physics/collision_types/#layer-based-collision","title":"Layer-Based Collision","text":"
    // Define layers\nnamespace GameLayers {\n    const pixelroot32::physics::CollisionLayer kPlayer = 1 << 0;\n    const pixelroot32::physics::CollisionLayer kEnemy = 1 << 1;\n    const pixelroot32::physics::CollisionLayer kObstacle = 1 << 2;\n    const pixelroot32::physics::CollisionLayer kProjectile = 1 << 3;\n}\n\n// Player collides with enemies and obstacles\nplayer->layer = GameLayers::kPlayer;\nplayer->mask = GameLayers::kEnemy | GameLayers::kObstacle;\n\n// Enemy collides with player and obstacles\nenemy->layer = GameLayers::kEnemy;\nenemy->mask = GameLayers::kPlayer | GameLayers::kObstacle;\n\n// Projectile collides with enemies only\nprojectile->layer = GameLayers::kProjectile;\nprojectile->mask = GameLayers::kEnemy;\n
    "},{"location":"api_reference/physics/collision_types/#circle-vs-circle-collision","title":"Circle vs Circle Collision","text":"
    bool checkCollision(const Circle& a, const Circle& b) {\n    return pixelroot32::physics::intersects(a, b);\n}\n
    "},{"location":"api_reference/physics/collision_types/#sweep-test-for-fast-projectiles","title":"Sweep Test for Fast Projectiles","text":"
    class ProjectileActor : public pixelroot32::core::Actor {\nprivate:\n    float startX, startY;\n    float endX, endY;\n    float radius = 2.0f;\n\npublic:\n    bool checkWallCollision(const Rect& wall) {\n        pixelroot32::physics::Circle start{startX, startY, radius};\n        pixelroot32::physics::Circle end{endX, endY, radius};\n\n        float tHit = 0.0f;\n        if (pixelroot32::physics::sweepCircleVsRect(start, end, wall, tHit)) {\n            // Collision detected at time tHit\n            return true;\n        }\n        return false;\n    }\n};\n
    "},{"location":"api_reference/physics/collision_types/#performance-considerations","title":"Performance Considerations","text":"
    • AABB checks: Very fast (simple rectangle intersection)
    • Circle checks: Slightly slower (distance calculation)
    • Sweep tests: More expensive (use only for fast-moving objects)
    • Layer filtering: Essential for performance with many actors
    "},{"location":"api_reference/physics/collision_types/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Float math: Uses floating point; acceptable but integer math would be faster
    • Sweep tests: Use sparingly (more CPU intensive)
    • Layer efficiency: Use layers effectively to minimize checks
    "},{"location":"api_reference/physics/collision_types/#see-also","title":"See Also","text":"
    • CollisionSystem - Collision detection system
    • Actor - Actors that use collision layers
    • Manual - Physics and Collisions
    • API Overview
    "},{"location":"api_reference/ui/ui_button/","title":"UIButton","text":"

    A clickable button UI element.

    "},{"location":"api_reference/ui/ui_button/#description","title":"Description","text":"

    UIButton is a clickable button that supports both physical (keyboard/gamepad) and touch input. It can trigger a callback function when pressed and integrates with UI layouts for automatic navigation.

    Buttons support selection state (for D-pad navigation), custom styling, and text alignment.

    "},{"location":"api_reference/ui/ui_button/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIButton : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_button/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    • Inherited by: Your custom button classes (if needed)
    "},{"location":"api_reference/ui/ui_button/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_button/#uibuttonstring-t-uint8_t-index-float-x-float-y-float-w-float-h-function-callback-textalignment-textalign-center-int-fontsize-2","title":"UIButton(string t, uint8_t index, float x, float y, float w, float h, function callback, TextAlignment textAlign = CENTER, int fontSize = 2)

    Constructs a new UIButton.

    Parameters: - t (std::string): Button label text - index (uint8_t): Navigation index (for D-pad navigation in layouts) - x (float): X position - y (float): Y position - w (float): Width - h (float): Height - callback (std::function): Function to call when clicked/pressed - textAlign (TextAlignment, optional): Text alignment. Default: CENTER - fontSize (int, optional): Text size multiplier. Default: 2

    Example:

    #include \"graphics/ui/UIButton.h\"\n\nvoid onStartButtonClicked() {\n    // Start game\n    engine.setScene(&gameScene);\n}\n\nvoid onQuitButtonClicked() {\n    // Quit game\n    engine.stop();\n}\n\n// Create buttons\npixelroot32::graphics::ui::UIButton* startButton = new pixelroot32::graphics::ui::UIButton(\n    \"Start\",\n    0,  // index\n    64.0f, 64.0f,  // position\n    100.0f, 30.0f, // size\n    onStartButtonClicked,\n    pixelroot32::graphics::ui::TextAlignment::CENTER,\n    2\n);\n\npixelroot32::graphics::ui::UIButton* quitButton = new pixelroot32::graphics::ui::UIButton(\n    \"Quit\",\n    1,  // index\n    64.0f, 100.0f,\n    100.0f, 30.0f,\n    onQuitButtonClicked\n);\n

    ","text":""},{"location":"api_reference/ui/ui_button/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_button/#void-setstylecolor-textcol-color-bgcol-bool-drawbg","title":"void setStyle(Color textCol, Color bgCol, bool drawBg)

    Configures the button's visual style.

    Parameters: - textCol (Color): Color of the text - bgCol (Color): Color of the background - drawBg (bool): Whether to draw the background rectangle

    Returns: - void

    Example:

    button->setStyle(\n    pixelroot32::graphics::Color::White,  // Text color\n    pixelroot32::graphics::Color::Blue,   // Background color\n    true                                   // Draw background\n);\n

    ","text":""},{"location":"api_reference/ui/ui_button/#void-setselectedbool-selected","title":"void setSelected(bool selected)

    Sets the selection state (e.g., focused via D-pad).

    Parameters: - selected (bool): true if selected

    Returns: - void

    Notes: - Used by layouts for D-pad navigation - Selected buttons typically have different visual style - Can be set manually or automatically by layouts

    Example:

    button->setSelected(true);  // Highlight button\n

    ","text":""},{"location":"api_reference/ui/ui_button/#bool-getselected-const","title":"bool getSelected() const

    Checks if the button is currently selected.

    Returns: - bool: true if selected

    Example:

    if (button->getSelected()) {\n    // Button is focused\n}\n

    ","text":""},{"location":"api_reference/ui/ui_button/#bool-isfocusable-const-override","title":"bool isFocusable() const override

    Returns true (Buttons are always focusable).

    Returns: - bool: Always true

    ","text":""},{"location":"api_reference/ui/ui_button/#void-handleinputconst-inputmanager-input","title":"void handleInput(const InputManager& input)

    Handles input events. Checks for touch events within bounds or confirmation buttons if selected.

    Parameters: - input (const pixelroot32::input::InputManager&): The input manager instance

    Returns: - void

    Notes: - Should be called every frame in update() - Checks if button is selected and action button is pressed - Triggers callback if conditions are met

    Example:

    void update(unsigned long deltaTime) override {\n    UIElement::update(deltaTime);\n\n    auto& input = engine.getInputManager();\n    button->handleInput(input);\n}\n

    ","text":""},{"location":"api_reference/ui/ui_button/#void-press","title":"void press()

    Manually triggers the button's action.

    Returns: - void

    Notes: - Calls the button's callback function - Useful for programmatic button presses

    Example:

    button->press();  // Trigger button action\n

    ","text":""},{"location":"api_reference/ui/ui_button/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override

    Updates the button state.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    Notes: - Called automatically by Scene if isEnabled is true - Handles input and updates button state - Override to add custom update logic

    Example:

    void update(unsigned long deltaTime) override {\n    UIButton::update(deltaTime);\n\n    // Custom update logic\n    if (shouldPulse) {\n        // Animate button\n    }\n}\n

    ","text":""},{"location":"api_reference/ui/ui_button/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override

    Renders the button.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): The renderer to use

    Returns: - void

    Notes: - Called automatically by Scene if isVisible is true - Draws background (if enabled) and text - Selected state may change appearance

    Example:

    // Drawing is handled automatically\n// Override only for custom rendering\n

    ","text":""},{"location":"api_reference/ui/ui_button/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIButton.h\"\n#include \"core/Scene.h\"\n\nclass MainMenuScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIButton* startButton;\n    pixelroot32::graphics::ui::UIButton* quitButton;\n\npublic:\n    void init() override {\n        // Start button\n        startButton = new pixelroot32::graphics::ui::UIButton(\n            \"Start Game\",\n            0,  // index\n            64.0f, 50.0f,  // position (centered)\n            120.0f, 30.0f, // size\n            [this]() {\n                // Lambda callback\n                engine.setScene(&gameScene);\n            },\n            pixelroot32::graphics::ui::TextAlignment::CENTER,\n            2\n        );\n        startButton->setStyle(\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Blue,\n            true\n        );\n        addEntity(startButton);\n\n        // Quit button\n        quitButton = new pixelroot32::graphics::ui::UIButton(\n            \"Quit\",\n            1,  // index\n            64.0f, 90.0f,\n            120.0f, 30.0f,\n            [this]() {\n                // Quit game\n                engine.stop();\n            }\n        );\n        quitButton->setStyle(\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Red,\n            true\n        );\n        addEntity(quitButton);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Buttons handle input automatically\n        // Layouts handle navigation automatically\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw background\n        renderer.drawFilledRectangle(0, 0, 128, 128, Color::Black);\n\n        // Draw UI elements (buttons)\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_button/#button-navigation","title":"Button Navigation","text":"

    Buttons can be navigated with D-pad when in layouts:

    // Buttons in a vertical layout\npixelroot32::graphics::ui::UIVerticalLayout* layout = new UIVerticalLayout(64.0f, 50.0f);\nlayout->addChild(startButton);  // Index 0\nlayout->addChild(quitButton);   // Index 1\n\n// D-pad navigation is automatic\n// UP/DOWN moves selection\n// Action button (A) triggers selected button\n
    "},{"location":"api_reference/ui/ui_button/#performance-considerations","title":"Performance Considerations","text":"
    • Input handling: handleInput() is fast; safe to call every frame
    • Rendering: Simple rectangle and text; very efficient
    • Memory: Each button consumes memory (stay within MAX_ENTITIES)
    "},{"location":"api_reference/ui/ui_button/#esp32-considerations","title":"ESP32 Considerations","text":"
    • String storage: Button labels use std::string; consider memory usage
    • Callback functions: Use function pointers or lambdas (both efficient)
    "},{"location":"api_reference/ui/ui_button/#see-also","title":"See Also","text":"
    • UIElement - Base UI element class
    • UILabel - Text label
    • UILayouts - Layout containers
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_checkbox/","title":"UICheckBox","text":"

    A clickable checkbox UI element.

    "},{"location":"api_reference/ui/ui_checkbox/#description","title":"Description","text":"

    UICheckBox is a clickable checkbox that can be toggled between checked and unchecked states. It supports both physical (keyboard/gamepad) and touch input. It can trigger a callback function when its state changes and integrates with UI layouts for automatic navigation.

    "},{"location":"api_reference/ui/ui_checkbox/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UICheckBox : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_checkbox/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    "},{"location":"api_reference/ui/ui_checkbox/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_checkbox/#uicheckboxstring-label-uint8_t-index-float-x-float-y-float-w-float-h-bool-checked-false-function-callback-nullptr-int-fontsize-2","title":"UICheckBox(string label, uint8_t index, float x, float y, float w, float h, bool checked = false, function callback = nullptr, int fontSize = 2)

    Constructs a new UICheckBox.

    Parameters: - label (std::string): Checkbox label text - index (uint8_t): Navigation index (for D-pad navigation in layouts) - x (float): X position - y (float): Y position - w (float): Width - h (float): Height - checked (bool, optional): Initial checked state. Default: false - callback (std::function, optional): Function to call when the state changes. Default: nullptr - fontSize (int, optional): Text size multiplier. Default: 2

    Example:

    #include \"graphics/ui/UICheckBox.h\"\n\nvoid onCheckChanged(bool checked) {\n    if (checked) {\n        // Sound enabled\n    } else {\n        // Sound disabled\n    }\n}\n\n// Create checkbox\npixelroot32::graphics::ui::UICheckBox* soundCheckbox = new pixelroot32::graphics::ui::UICheckBox(\n    \"Enable Sound\",\n    0,  // index\n    64.0f, 64.0f,  // position\n    120.0f, 20.0f, // size\n    true,          // initial state\n    onCheckChanged,\n    1              // font size\n);\n

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_checkbox/#void-setstylecolor-textcol-color-bgcol-bool-drawbg-false","title":"void setStyle(Color textCol, Color bgCol, bool drawBg = false)

    Configures the checkbox's visual style.

    Parameters: - textCol (Color): Color of the text - bgCol (Color): Color of the background - drawBg (bool, optional): Whether to draw the background rectangle. Default: false

    Returns: - void

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#void-setcheckedbool-checked","title":"void setChecked(bool checked)

    Sets the checked state.

    Parameters: - checked (bool): True if checked

    Returns: - void

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#bool-ischecked-const","title":"bool isChecked() const

    Checks if the checkbox is currently checked.

    Returns: - bool: true if checked

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#void-toggle","title":"void toggle()

    Toggles the checkbox state and triggers the callback.

    Returns: - void

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#void-setselectedbool-selected","title":"void setSelected(bool selected)

    Sets the selection state (e.g., focused via D-pad).

    Parameters: - selected (bool): True if selected

    Returns: - void

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#bool-getselected-const","title":"bool getSelected() const

    Checks if the checkbox is currently selected.

    Returns: - bool: true if selected

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#callbacks","title":"Callbacks","text":""},{"location":"api_reference/ui/ui_checkbox/#oncheckchanged","title":"onCheckChanged

    The onCheckChanged callback is a std::function<void(bool)> that is triggered whenever the checkbox state changes via setChecked() or toggle().

    checkbox->onCheckChanged = [](bool isChecked) {\n    Serial.println(isChecked ? \"Checked!\" : \"Unchecked!\");\n};\n
    ","text":""},{"location":"api_reference/ui/ui_checkbox/#navigation-layouts","title":"Navigation & Layouts","text":"

    UICheckBox is designed to work seamlessly with UILayout containers (like UIVerticalLayout).

    • Focusable: Returns true for isFocusable(), allowing it to receive focus in a layout.
    • Input Handling: When selected (focused), it listens for the button index provided in the constructor (typically the 'A' button) to toggle its state.
    • Visual Feedback: When selected, it displays a selection indicator (usually a > character) if no background is drawn, or highlights its text/border.
    "},{"location":"api_reference/ui/ui_element/","title":"UIElement","text":"

    Base class for all user interface elements.

    "},{"location":"api_reference/ui/ui_element/#description","title":"Description","text":"

    UIElement is the base class for all UI components (buttons, labels, panels, etc.). It inherits from Entity to integrate with the scene graph and automatically sets the entity type to UI_ELEMENT and render layer to 2 (UI layer).

    "},{"location":"api_reference/ui/ui_element/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    enum class UIElementType {\n        GENERIC,\n        BUTTON,\n        LABEL,\n        CHECKBOX,\n        LAYOUT\n    };\n\n    class UIElement : public pixelroot32::core::Entity {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_element/#inheritance","title":"Inheritance","text":"
    • Inherits from: pixelroot32::core::Entity
    • Inherited by: UIButton, UICheckBox, UILabel, UIPanel, and all UI layouts
    "},{"location":"api_reference/ui/ui_element/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_element/#uielementfloat-x-float-y-float-w-float-h-uielementtype-type-uielementtypegeneric","title":"UIElement(float x, float y, float w, float h, UIElementType type = UIElementType::GENERIC)","text":"

    Constructs a new UIElement.

    Parameters: - x (float): X position - y (float): Y position - w (float): Width - h (float): Height - type (UIElementType): The type of the element (default: GENERIC)

    Notes: - Entity type is automatically set to UI_ELEMENT - Render layer is automatically set to 2 (UI layer) - Position and size can be modified after construction

    Example:

    class MyUIElement : public pixelroot32::graphics::ui::UIElement {\npublic:\n    MyUIElement(float x, float y) \n        : UIElement(x, y, 100.0f, 50.0f) {}\n\n    void update(unsigned long deltaTime) override {\n        // UI logic\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // UI rendering\n    }\n};\n

    "},{"location":"api_reference/ui/ui_element/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_element/#uielementtype-gettype-const","title":"UIElementType getType() const","text":"

    Returns the type of the UI element.

    Returns: - UIElementType: The type enum value (GENERIC, BUTTON, LABEL, CHECKBOX, or LAYOUT)

    "},{"location":"api_reference/ui/ui_element/#virtual-bool-isfocusable-const","title":"virtual bool isFocusable() const","text":"

    Checks if the element is focusable/selectable. Useful for navigation logic.

    Returns: - bool: true if focusable, false otherwise (default: false)

    "},{"location":"api_reference/ui/ui_element/#void-setpositionfloat-newx-float-newy","title":"void setPosition(float newX, float newY)","text":"

    Sets the position of the element.

    Parameters: - newX (float): New X coordinate - newY (float): New Y coordinate

    Returns: - void

    Notes: - Updates element position immediately - Use for manual positioning or animations

    Example:

    uiElement->setPosition(100.0f, 50.0f);\n

    "},{"location":"api_reference/ui/ui_element/#virtual-void-getpreferredsizefloat-preferredwidth-float-preferredheight-const","title":"virtual void getPreferredSize(float& preferredWidth, float& preferredHeight) const","text":"

    Gets the preferred size of the element. Used by layouts to determine how much space the element needs.

    Parameters: - preferredWidth (float&): Output parameter for preferred width (or -1 if flexible) - preferredHeight (float&): Output parameter for preferred height (or -1 if flexible)

    Returns: - void

    Notes: - Default implementation returns current width/height - Override in derived classes for custom sizing logic - Layouts use this to arrange elements

    Example:

    void getPreferredSize(float& w, float& h) const override {\n    // Custom sizing logic\n    w = static_cast<float>(width);\n    h = static_cast<float>(height);\n}\n

    "},{"location":"api_reference/ui/ui_element/#inherited-from-entity","title":"Inherited from Entity","text":"

    UIElement inherits all properties and methods from Entity:

    • float x, y: Position
    • int width, height: Dimensions
    • bool isVisible: Visibility state
    • bool isEnabled: Enabled state
    • unsigned char renderLayer: Render layer (automatically set to 2)
    • void setVisible(bool v): Set visibility
    • void setEnabled(bool e): Set enabled state
    • virtual void update(unsigned long deltaTime): Update logic
    • virtual void draw(Renderer& renderer): Drawing logic
    "},{"location":"api_reference/ui/ui_element/#textalignment-enum","title":"TextAlignment Enum","text":"

    Text alignment options for UI elements.

    Values: - TextAlignment::LEFT: Left-aligned text - TextAlignment::CENTER: Center-aligned text - TextAlignment::RIGHT: Right-aligned text

    "},{"location":"api_reference/ui/ui_element/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIElement.h\"\n\nclass CustomUIElement : public pixelroot32::graphics::ui::UIElement {\nprivate:\n    pixelroot32::graphics::Color bgColor;\n\npublic:\n    CustomUIElement(float x, float y, float w, float h) \n        : UIElement(x, y, w, h),\n          bgColor(pixelroot32::graphics::Color::Blue) {}\n\n    void update(unsigned long deltaTime) override {\n        // Update logic (if needed)\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        if (isVisible) {\n            // Draw background\n            renderer.drawFilledRectangle(\n                static_cast<int>(x),\n                static_cast<int>(y),\n                width,\n                height,\n                bgColor\n            );\n\n            // Draw border\n            renderer.drawRectangle(\n                static_cast<int>(x),\n                static_cast<int>(y),\n                width,\n                height,\n                pixelroot32::graphics::Color::White\n            );\n        }\n    }\n\n    void getPreferredSize(float& w, float& h) const override {\n        w = static_cast<float>(width);\n        h = static_cast<float>(height);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_element/#performance-considerations","title":"Performance Considerations","text":"
    • Render layer: UI elements are on layer 2, drawn after gameplay
    • Visibility: Use isVisible = false to hide elements efficiently
    • Layout integration: Layouts automatically manage element positioning
    "},{"location":"api_reference/ui/ui_element/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Each UI element consumes memory (stay within MAX_ENTITIES)
    • Object pooling: Reuse UI elements when possible
    • Update frequency: Disable UI elements that don't need to update
    "},{"location":"api_reference/ui/ui_element/#see-also","title":"See Also","text":"
    • Entity - Base entity class
    • UIButton - Clickable button
    • UILabel - Text label
    • UILayout - Layout containers
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_label/","title":"UILabel","text":"

    A simple text label UI element.

    "},{"location":"api_reference/ui/ui_label/#description","title":"Description","text":"

    UILabel displays a string of text on the screen. It auto-calculates its bounds based on text length and font size, making it easy to create dynamic text displays.

    Labels are useful for displaying scores, status messages, menu text, and other UI information.

    "},{"location":"api_reference/ui/ui_label/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UILabel : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_label/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    • Inherited by: Your custom label classes (if needed)
    "},{"location":"api_reference/ui/ui_label/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_label/#uilabelstring-t-float-x-float-y-color-col-uint8_t-sz","title":"UILabel(string t, float x, float y, Color col, uint8_t sz)","text":"

    Constructs a new UILabel.

    Parameters: - t (std::string): Initial text - x (float): X position - y (float): Y position - col (Color): Text color - sz (uint8_t): Text size multiplier

    Example:

    #include \"graphics/ui/UILabel.h\"\n\n// Create label\npixelroot32::graphics::ui::UILabel* scoreLabel = new pixelroot32::graphics::ui::UILabel(\n    \"Score: 0\",\n    10.0f, 10.0f,  // position\n    pixelroot32::graphics::Color::White,\n    1  // size\n);\n\n// Add to scene\nscene->addEntity(scoreLabel);\n

    "},{"location":"api_reference/ui/ui_label/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_label/#void-settextconst-string-t","title":"void setText(const string& t)","text":"

    Updates the label's text. Recalculates dimensions immediately using the active font metrics to ensure precise layout.

    Parameters: - t (const std::string&): New text

    Returns: - void

    Notes: - Automatically recalculates width and height using FontManager::textWidth - Use for dynamic text (scores, timers, etc.) - Text is stored as std::string (consider memory on ESP32)

    Example:

    // Update score label\nchar scoreText[32];\nsnprintf(scoreText, sizeof(scoreText), \"Score: %d\", score);\nscoreLabel->setText(scoreText);\n

    "},{"location":"api_reference/ui/ui_label/#void-setvisiblebool-v","title":"void setVisible(bool v)","text":"

    Sets visibility.

    Parameters: - v (bool): true to show, false to hide

    Returns: - void

    Notes: - Inherited from Entity - Hides label without removing from scene

    Example:

    label->setVisible(false);  // Hide\nlabel->setVisible(true);   // Show\n

    "},{"location":"api_reference/ui/ui_label/#void-centerxint-screenwidth","title":"void centerX(int screenWidth)","text":"

    Centers the label horizontally within a given width (typically the screen width). Recalculates dimensions before positioning to ensure perfect centering.

    Parameters: - screenWidth (int): The width to center within (e.g., engine.getRenderer().getWidth())

    Returns: - void

    Example:

    label->centerX(128); // Center on a 128px screen\n

    "},{"location":"api_reference/ui/ui_label/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the label state.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    Notes: - Called automatically by Scene if isEnabled is true - Default implementation does nothing - Override to add custom update logic (animations, etc.)

    Example:

    void update(unsigned long deltaTime) override {\n    UILabel::update(deltaTime);\n\n    // Custom logic (e.g., update text based on game state)\n    if (scoreChanged) {\n        updateScoreText();\n    }\n}\n

    "},{"location":"api_reference/ui/ui_label/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Renders the label.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): The renderer to use

    Returns: - void

    Notes: - Called automatically by Scene if isVisible is true - Draws text at label position - Uses default font

    Example:

    // Drawing is handled automatically\n// Override only for custom rendering\n

    "},{"location":"api_reference/ui/ui_label/#auto-sizing","title":"Auto-Sizing","text":"

    Labels automatically calculate their size based on text:

    • Width: text.length() * (6 * size) pixels
    • Height: 8 * size pixels

    Example:

    // Label with text \"Hello\" (5 characters), size 1\n// Width: 5 * 6 * 1 = 30 pixels\n// Height: 8 * 1 = 8 pixels\n\nUILabel label(\"Hello\", 10, 10, Color::White, 1);\n// label.width = 30, label.height = 8\n

    "},{"location":"api_reference/ui/ui_label/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UILabel.h\"\n\nclass GameScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UILabel* scoreLabel;\n    pixelroot32::graphics::ui::UILabel* livesLabel;\n    int score = 0;\n    int lives = 3;\n\npublic:\n    void init() override {\n        // Score label\n        scoreLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Score: 0\",\n            10.0f, 10.0f,\n            pixelroot32::graphics::Color::White,\n            1\n        );\n        addEntity(scoreLabel);\n\n        // Lives label\n        livesLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Lives: 3\",\n            10.0f, 25.0f,\n            pixelroot32::graphics::Color::Yellow,\n            1\n        );\n        addEntity(livesLabel);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Update labels\n        char text[32];\n        snprintf(text, sizeof(text), \"Score: %d\", score);\n        scoreLabel->setText(text);\n\n        snprintf(text, sizeof(text), \"Lives: %d\", lives);\n        livesLabel->setText(text);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw game\n        Scene::draw(renderer);\n\n        // Labels are drawn automatically by Scene::draw()\n    }\n};\n
    "},{"location":"api_reference/ui/ui_label/#centered-labels","title":"Centered Labels","text":"
    // Create centered title\npixelroot32::graphics::ui::UILabel* title = new pixelroot32::graphics::ui::UILabel(\n    \"Game Title\",\n    0, 20.0f,  // X will be adjusted\n    pixelroot32::graphics::Color::Yellow,\n    2  // Large text\n);\ntitle->centerX(128);  // Center on screen\naddEntity(title);\n
    "},{"location":"api_reference/ui/ui_label/#performance-considerations","title":"Performance Considerations","text":"
    • Text updates: setText() recalculates size; avoid calling every frame if text doesn't change
    • String storage: Uses std::string; consider memory on ESP32
    • Rendering: Simple text drawing; very efficient
    • Static text: For static text, create once and don't update
    "},{"location":"api_reference/ui/ui_label/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: std::string uses heap memory; use static buffers when possible
    • Text updates: Limit frequency of text updates
    • String length: Keep text short to save memory
    "},{"location":"api_reference/ui/ui_label/#see-also","title":"See Also","text":"
    • UIElement - Base UI element class
    • UIButton - Clickable button
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layout/","title":"UILayout","text":"

    Base class for UI layout containers.

    "},{"location":"api_reference/ui/ui_layout/#description","title":"Description","text":"

    UILayout is the base class for all layout containers. Layouts organize UI elements automatically, handling positioning, spacing, and optional scrolling. Layouts are themselves UI elements that can be added to scenes.

    "},{"location":"api_reference/ui/ui_layout/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UILayout : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layout/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    • Inherited by: UIVerticalLayout, UIHorizontalLayout, UIGridLayout, UIAnchorLayout
    "},{"location":"api_reference/ui/ui_layout/#scrollbehavior-enum","title":"ScrollBehavior Enum","text":"

    Defines how scrolling behaves in layouts.

    Values: - ScrollBehavior::NONE: No scrolling allowed - ScrollBehavior::SCROLL: Scroll freely within bounds - ScrollBehavior::CLAMP: Scroll but clamp to content bounds

    "},{"location":"api_reference/ui/ui_layout/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layout/#virtual-void-addelementuielement-element-0","title":"virtual void addElement(UIElement* element) = 0","text":"

    Adds a UI element to the layout. Must be implemented by derived classes.

    Parameters: - element (UIElement*): Pointer to the element to add

    "},{"location":"api_reference/ui/ui_layout/#virtual-void-removeelementuielement-element-0","title":"virtual void removeElement(UIElement* element) = 0","text":"

    Removes a UI element from the layout. Must be implemented by derived classes.

    Parameters: - element (UIElement*): Pointer to the element to remove

    "},{"location":"api_reference/ui/ui_layout/#virtual-void-updatelayout-0","title":"virtual void updateLayout() = 0","text":"

    Recalculates positions of all elements in the layout. Must be implemented by derived classes.

    Returns: - void

    Notes: - Should be called automatically when elements are added/removed

    "},{"location":"api_reference/ui/ui_layout/#virtual-void-handleinputconst-inputmanager-input-0","title":"virtual void handleInput(const InputManager& input) = 0","text":"

    Handles input for layout navigation (scroll, selection, etc.). Must be implemented by derived classes.

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    "},{"location":"api_reference/ui/ui_layout/#void-setpaddingfloat-p","title":"void setPadding(float p)","text":"

    Sets the padding (internal spacing) of the layout.

    Parameters: - p (float): Padding value in pixels

    Returns: - void

    Notes: - Layout is automatically recalculated

    "},{"location":"api_reference/ui/ui_layout/#float-getpadding-const","title":"float getPadding() const","text":"

    Gets the current padding.

    Returns: - float: Padding value in pixels

    "},{"location":"api_reference/ui/ui_layout/#void-setspacingfloat-s","title":"void setSpacing(float s)","text":"

    Sets the spacing between elements.

    Parameters: - s (float): Spacing value in pixels

    Returns: - void

    Notes: - Layout is automatically recalculated - Default: 4.0 pixels

    "},{"location":"api_reference/ui/ui_layout/#float-getspacing-const","title":"float getSpacing() const","text":"

    Gets the current spacing.

    Returns: - float: Spacing value in pixels

    "},{"location":"api_reference/ui/ui_layout/#size_t-getelementcount-const","title":"size_t getElementCount() const","text":"

    Gets the number of elements in the layout.

    Returns: - size_t: Element count

    "},{"location":"api_reference/ui/ui_layout/#uielement-getelementsize_t-index-const","title":"UIElement* getElement(size_t index) const","text":"

    Gets the element at a specific index.

    Parameters: - index (size_t): Element index

    Returns: - UIElement*: Pointer to the element, or nullptr if index is invalid

    "},{"location":"api_reference/ui/ui_layout/#void-clearelements","title":"void clearElements()","text":"

    Clears all elements from the layout.

    Returns: - void

    Notes: - Elements are not deleted (you must manage their lifetimes) - Layout is automatically recalculated

    "},{"location":"api_reference/ui/ui_layout/#protected-members","title":"Protected Members","text":"
    • std::vector<UIElement*> elements: List of child elements
    • float padding: Internal padding
    • float spacing: Spacing between elements (default: 4.0)
    • float scrollOffset: Current scroll offset
    • bool enableScroll: Whether scrolling is enabled
    • ScrollBehavior scrollBehavior: Scroll behavior mode
    "},{"location":"api_reference/ui/ui_layout/#see-also","title":"See Also","text":"
    • UIElement - Base UI element class
    • UIVerticalLayout - Vertical layout
    • UIHorizontalLayout - Horizontal layout
    • UIGridLayout - Grid layout
    • UIAnchorLayout - Anchor layout
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/","title":"UIAnchorLayout","text":"

    Layout that positions elements at fixed anchor points on the screen.

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#description","title":"Description","text":"

    UIAnchorLayout positions UI elements at fixed anchor points (corners, center, edges) without reflow. Very efficient for HUDs, debug UI, and fixed-position elements. Positions are calculated once or when screen size changes.

    This layout is ideal for HUD elements like score, lives, health bars, and other fixed-position UI.

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIAnchorLayout : public UILayout {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#inheritance","title":"Inheritance","text":"
    • Inherits from: UILayout
    • Inherited by: Your custom anchor layouts (if needed)
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#anchor-enum","title":"Anchor Enum","text":"

    Defines anchor points for positioning UI elements.

    Values: - Anchor::TOP_LEFT: Top-left corner - Anchor::TOP_RIGHT: Top-right corner - Anchor::BOTTOM_LEFT: Bottom-left corner - Anchor::BOTTOM_RIGHT: Bottom-right corner - Anchor::CENTER: Center of screen - Anchor::TOP_CENTER: Top center - Anchor::BOTTOM_CENTER: Bottom center - Anchor::LEFT_CENTER: Left center - Anchor::RIGHT_CENTER: Right center

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/anchor_layout/#uianchorlayoutfloat-x-float-y-float-w-float-h","title":"UIAnchorLayout(float x, float y, float w, float h)","text":"

    Constructs a new UIAnchorLayout.

    Parameters: - x (float): X position of the layout container (usually 0) - y (float): Y position of the layout container (usually 0) - w (float): Width of the layout container (usually screen width) - h (float): Height of the layout container (usually screen height)

    Example:

    #include \"graphics/ui/UIAnchorLayout.h\"\n\n// Create full-screen anchor layout for HUD\nauto& renderer = engine.getRenderer();\npixelroot32::graphics::ui::UIAnchorLayout* hud = \n    new pixelroot32::graphics::ui::UIAnchorLayout(\n        0.0f, 0.0f,\n        static_cast<float>(renderer.getWidth()),\n        static_cast<float>(renderer.getHeight())\n    );\nhud->setScreenSize(\n    static_cast<float>(renderer.getWidth()),\n    static_cast<float>(renderer.getHeight())\n);\n

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-addelementuielement-element-anchor-anchor","title":"void addElement(UIElement* element, Anchor anchor)","text":"

    Adds a UI element with a specific anchor point.

    Parameters: - element (UIElement*): Pointer to the element to add - anchor (Anchor): Anchor point for positioning

    Returns: - void

    Example:

    // Score label at top-right\nhud->addElement(scoreLabel, pixelroot32::graphics::ui::Anchor::TOP_RIGHT);\n\n// Lives label at top-left\nhud->addElement(livesLabel, pixelroot32::graphics::ui::Anchor::TOP_LEFT);\n

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-addelementuielement-element","title":"void addElement(UIElement* element)","text":"

    Adds a UI element to the layout (defaults to TOP_LEFT anchor).

    Parameters: - element (UIElement*): Pointer to the element to add

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-removeelementuielement-element","title":"void removeElement(UIElement* element)","text":"

    Removes a UI element from the layout.

    Parameters: - element (UIElement*): Pointer to the element to remove

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-updatelayout","title":"void updateLayout()","text":"

    Recalculates positions of all elements based on anchors.

    Returns: - void

    Notes: - Called automatically when screen size changes - Can be called manually if needed

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-handleinputconst-inputmanager-input","title":"void handleInput(const InputManager& input)","text":"

    Handles input (no-op for anchor layout, elements handle their own input).

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    Returns: - void

    Notes: - Anchor layout doesn't handle navigation - Elements handle their own input

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the layout and child elements.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws all elements.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-setscreensizefloat-screenwidth-float-screenheight","title":"void setScreenSize(float screenWidth, float screenHeight)","text":"

    Sets the screen size for anchor calculations.

    Parameters: - screenWidth (float): Screen width in pixels - screenHeight (float): Screen height in pixels

    Returns: - void

    Notes: - Used to calculate anchor positions - Should match actual display size - Layout is automatically recalculated

    Example:

    auto& renderer = engine.getRenderer();\nhud->setScreenSize(\n    static_cast<float>(renderer.getWidth()),\n    static_cast<float>(renderer.getHeight())\n);\n

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#float-getscreenwidth-const","title":"float getScreenWidth() const","text":"

    Gets the screen width.

    Returns: - float: Screen width in pixels

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#float-getscreenheight-const","title":"float getScreenHeight() const","text":"

    Gets the screen height.

    Returns: - float: Screen height in pixels

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIAnchorLayout.h\"\n\nclass GameScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIAnchorLayout* hud;\n    pixelroot32::graphics::ui::UILabel* scoreLabel;\n    pixelroot32::graphics::ui::UILabel* livesLabel;\n\npublic:\n    void init() override {\n        auto& renderer = engine.getRenderer();\n\n        // Create HUD layout\n        hud = new pixelroot32::graphics::ui::UIAnchorLayout(\n            0.0f, 0.0f,\n            static_cast<float>(renderer.getWidth()),\n            static_cast<float>(renderer.getHeight())\n        );\n        hud->setScreenSize(\n            static_cast<float>(renderer.getWidth()),\n            static_cast<float>(renderer.getHeight())\n        );\n\n        // Score label (top-right)\n        scoreLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Score: 0\",\n            0, 0,\n            Color::White,\n            1\n        );\n        hud->addElement(scoreLabel, \n                        pixelroot32::graphics::ui::Anchor::TOP_RIGHT);\n\n        // Lives label (top-left)\n        livesLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Lives: 3\",\n            0, 0,\n            Color::Yellow,\n            1\n        );\n        hud->addElement(livesLabel, \n                        pixelroot32::graphics::ui::Anchor::TOP_LEFT);\n\n        addEntity(hud);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Update labels\n        char text[32];\n        snprintf(text, sizeof(text), \"Score: %d\", score);\n        scoreLabel->setText(text);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw game\n        Scene::draw(renderer);\n\n        // HUD is drawn automatically (on layer 2)\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#anchor-positioning","title":"Anchor Positioning","text":"

    Elements are positioned based on their anchor:

    • TOP_LEFT: Element's top-left at screen top-left
    • TOP_RIGHT: Element's top-right at screen top-right
    • BOTTOM_LEFT: Element's bottom-left at screen bottom-left
    • BOTTOM_RIGHT: Element's bottom-right at screen bottom-right
    • CENTER: Element centered on screen
    • TOP_CENTER: Element centered horizontally, top-aligned
    • BOTTOM_CENTER: Element centered horizontally, bottom-aligned
    • LEFT_CENTER: Element centered vertically, left-aligned
    • RIGHT_CENTER: Element centered vertically, right-aligned
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#performance-considerations","title":"Performance Considerations","text":"
    • No reflow: Very efficient (positions calculated once)
    • Fixed positions: Ideal for HUD elements
    • Viewport independent: Elements stay in fixed screen positions
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Very efficient (no complex calculations)
    • Update frequency: Positions only recalculate when screen size changes
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#see-also","title":"See Also","text":"
    • UILayout - Base layout class
    • UILabel - Labels for HUD
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/grid_layout/","title":"UIGridLayout","text":"

    Grid layout container for organizing elements in a matrix.

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#description","title":"Description","text":"

    UIGridLayout organizes UI elements in a fixed grid of rows and columns. It supports navigation in 4 directions (UP/DOWN/LEFT/RIGHT) and automatic positioning based on grid coordinates.

    This layout is ideal for inventories, level selection screens, galleries, and any grid-based UI arrangement.

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIGridLayout : public UILayout {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#inheritance","title":"Inheritance","text":"
    • Inherits from: UILayout
    • Inherited by: Your custom grid layouts (if needed)
    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/grid_layout/#uigridlayoutfloat-x-float-y-float-w-float-h","title":"UIGridLayout(float x, float y, float w, float h)","text":"

    Constructs a new UIGridLayout.

    Parameters: - x (float): X position of the layout container - y (float): Y position of the layout container - w (float): Width of the layout container - h (float): Height of the layout container

    Example:

    #include \"graphics/ui/UIGridLayout.h\"\n\n// Create 4x4 grid for inventory\npixelroot32::graphics::ui::UIGridLayout* inventory = \n    new pixelroot32::graphics::ui::UIGridLayout(\n        10.0f, 10.0f,  // position\n        108.0f, 108.0f // size\n    );\ninventory->setColumns(4);\n

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-addelementuielement-element","title":"void addElement(UIElement* element)","text":"

    Adds a UI element to the layout.

    Parameters: - element (UIElement*): Pointer to the element to add

    Returns: - void

    Notes: - Elements are arranged in grid order (left to right, top to bottom) - Layout is automatically recalculated - Cell size is calculated based on columns and layout size

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-removeelementuielement-element","title":"void removeElement(UIElement* element)","text":"

    Removes a UI element from the layout.

    Parameters: - element (UIElement*): Pointer to the element to remove

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-updatelayout","title":"void updateLayout()","text":"

    Recalculates positions of all elements.

    Returns: - void

    Notes: - Recalculates cell dimensions - Recalculates row count - Repositions all elements

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-handleinputconst-inputmanager-input","title":"void handleInput(const InputManager& input)","text":"

    Handles input for navigation and selection.

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    Returns: - void

    Notes: - Handles UP/DOWN/LEFT/RIGHT navigation - Manages selection state - Wraps around grid edges (if configured)

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the layout and child elements.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws the layout and its visible elements.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-setcolumnsuint8_t-cols","title":"void setColumns(uint8_t cols)","text":"

    Sets the number of columns in the grid.

    Parameters: - cols (uint8_t): Number of columns (must be > 0)

    Returns: - void

    Notes: - Layout is automatically recalculated - Row count is calculated based on element count and columns

    Example:

    inventory->setColumns(4);  // 4 columns\n

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#uint8_t-getcolumns-const","title":"uint8_t getColumns() const","text":"

    Gets the number of columns.

    Returns: - uint8_t: Number of columns

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#uint8_t-getrows-const","title":"uint8_t getRows() const","text":"

    Gets the number of rows (calculated).

    Returns: - uint8_t: Number of rows

    Notes: - Calculated as ceil(elementCount / columns)

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#int-getselectedindex-const","title":"int getSelectedIndex() const","text":"

    Gets the currently selected element index.

    Returns: - int: Selected index, or -1 if none selected

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-setselectedindexint-index","title":"void setSelectedIndex(int index)","text":"

    Sets the selected element index.

    Parameters: - index (int): Index to select (-1 to deselect)

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#uielement-getselectedelement-const","title":"UIElement* getSelectedElement() const","text":"

    Gets the selected element.

    Returns: - UIElement*: Pointer to selected element, or nullptr if none selected

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-setnavigationbuttonsuint8_t-upbutton-uint8_t-downbutton-uint8_t-leftbutton-uint8_t-rightbutton","title":"void setNavigationButtons(uint8_t upButton, uint8_t downButton, uint8_t leftButton, uint8_t rightButton)","text":"

    Sets the navigation button indices.

    Parameters: - upButton (uint8_t): Button index for UP navigation - downButton (uint8_t): Button index for DOWN navigation - leftButton (uint8_t): Button index for LEFT navigation - rightButton (uint8_t): Button index for RIGHT navigation

    Returns: - void

    Notes: - Default: UP=0, DOWN=1, LEFT=2, RIGHT=3 - Change if your input mapping differs

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-setbuttonstylecolor-selectedtextcol-color-selectedbgcol-color-unselectedtextcol-color-unselectedbgcol","title":"void setButtonStyle(Color selectedTextCol, Color selectedBgCol, Color unselectedTextCol, Color unselectedBgCol)","text":"

    Sets the style colors for selected and unselected buttons.

    Parameters: - selectedTextCol (Color): Text color when selected - selectedBgCol (Color): Background color when selected - unselectedTextCol (Color): Text color when not selected - unselectedBgCol (Color): Background color when not selected

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIGridLayout.h\"\n\nclass InventoryScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIGridLayout* inventory;\n\npublic:\n    void init() override {\n        // Create 4x4 inventory grid\n        inventory = new pixelroot32::graphics::ui::UIGridLayout(\n            10.0f, 10.0f,\n            108.0f, 108.0f\n        );\n        inventory->setColumns(4);\n        inventory->setSpacing(2.0f);\n        inventory->setPadding(4.0f);\n\n        // Add inventory slots\n        for (int i = 0; i < 16; i++) {\n            auto* slot = createInventorySlot(i);\n            inventory->addElement(slot);\n        }\n\n        addEntity(inventory);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#see-also","title":"See Also","text":"
    • UILayout - Base layout class (abstract)
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/","title":"UIHorizontalLayout","text":"

    Horizontal layout container with scroll support.

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#description","title":"Description","text":"

    UIHorizontalLayout organizes UI elements horizontally, one next to another. It supports scrolling when content exceeds the visible viewport and handles keyboard/D-pad navigation automatically.

    This layout is ideal for toolbars, tab bars, horizontal menus, and any horizontal arrangement of UI elements.

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIHorizontalLayout : public UILayout {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#inheritance","title":"Inheritance","text":"
    • Inherits from: UILayout
    • Inherited by: Your custom horizontal layouts (if needed)
    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#uihorizontallayoutfloat-x-float-y-float-w-float-h","title":"UIHorizontalLayout(float x, float y, float w, float h)","text":"

    Constructs a new UIHorizontalLayout.

    Parameters: - x (float): X position of the layout container - y (float): Y position of the layout container - w (float): Width of the layout container (viewport width) - h (float): Height of the layout container

    Example:

    #include \"graphics/ui/UIHorizontalLayout.h\"\n\n// Create horizontal layout for toolbar\npixelroot32::graphics::ui::UIHorizontalLayout* toolbar = \n    new pixelroot32::graphics::ui::UIHorizontalLayout(\n        0.0f, 0.0f,   // position (top of screen)\n        128.0f, 20.0f // size\n    );\n

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-addelementuielement-element","title":"void addElement(UIElement* element)","text":"

    Adds a UI element to the layout.

    Parameters: - element (UIElement*): Pointer to the element to add

    Returns: - void

    Notes: - Elements are arranged horizontally, left to right - Layout is automatically recalculated - Elements are positioned based on spacing and padding

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-removeelementuielement-element","title":"void removeElement(UIElement* element)","text":"

    Removes a UI element from the layout.

    Parameters: - element (UIElement*): Pointer to the element to remove

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-updatelayout","title":"void updateLayout()","text":"

    Recalculates positions of all elements.

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-handleinputconst-inputmanager-input","title":"void handleInput(const InputManager& input)","text":"

    Handles input for navigation and scrolling.

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    Returns: - void

    Notes: - Handles LEFT/RIGHT navigation - Manages selection state - Handles scrolling if enabled

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the layout (handles smooth scrolling).

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws the layout and its visible elements.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setscrollenabledbool-enable","title":"void setScrollEnabled(bool enable)","text":"

    Enables or disables scrolling.

    Parameters: - enable (bool): true to enable scrolling

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setviewportwidthfloat-w","title":"void setViewportWidth(float w)","text":"

    Sets the viewport width (visible area).

    Parameters: - w (float): Viewport width in pixels

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#float-getscrolloffset-const","title":"float getScrollOffset() const","text":"

    Gets the current scroll offset.

    Returns: - float: Scroll offset in pixels

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setscrolloffsetfloat-offset","title":"void setScrollOffset(float offset)","text":"

    Sets the scroll offset directly.

    Parameters: - offset (float): Scroll offset in pixels

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#float-getcontentwidth-const","title":"float getContentWidth() const","text":"

    Gets the total content width.

    Returns: - float: Content width in pixels

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#int-getselectedindex-const","title":"int getSelectedIndex() const","text":"

    Gets the currently selected element index.

    Returns: - int: Selected index, or -1 if none selected

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setselectedindexint-index","title":"void setSelectedIndex(int index)","text":"

    Sets the selected element index.

    Parameters: - index (int): Index to select (-1 to deselect)

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#uielement-getselectedelement-const","title":"UIElement* getSelectedElement() const","text":"

    Gets the selected element.

    Returns: - UIElement*: Pointer to selected element, or nullptr if none selected

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setscrollspeedfloat-speed","title":"void setScrollSpeed(float speed)","text":"

    Sets the scroll speed for smooth scrolling.

    Parameters: - speed (float): Pixels per millisecond

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setnavigationbuttonsuint8_t-leftbutton-uint8_t-rightbutton","title":"void setNavigationButtons(uint8_t leftButton, uint8_t rightButton)","text":"

    Sets the navigation button indices.

    Parameters: - leftButton (uint8_t): Button index for LEFT navigation - rightButton (uint8_t): Button index for RIGHT navigation

    Returns: - void

    Notes: - Default: LEFT = 2, RIGHT = 3 - Change if your input mapping differs

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setbuttonstylecolor-selectedtextcol-color-selectedbgcol-color-unselectedtextcol-color-unselectedbgcol","title":"void setButtonStyle(Color selectedTextCol, Color selectedBgCol, Color unselectedTextCol, Color unselectedBgCol)","text":"

    Sets the style colors for selected and unselected buttons.

    Parameters: - selectedTextCol (Color): Text color when selected - selectedBgCol (Color): Background color when selected - unselectedTextCol (Color): Text color when not selected - unselectedBgCol (Color): Background color when not selected

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIHorizontalLayout.h\"\n\nclass ToolbarScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIHorizontalLayout* toolbar;\n\npublic:\n    void init() override {\n        // Create horizontal toolbar\n        toolbar = new pixelroot32::graphics::ui::UIHorizontalLayout(\n            0.0f, 0.0f,    // Top of screen\n            128.0f, 20.0f  // Full width, 20px tall\n        );\n        toolbar->setSpacing(4.0f);\n        toolbar->setPadding(2.0f);\n\n        // Add toolbar buttons\n        toolbar->addElement(newButton(\"File\", ...));\n        toolbar->addElement(newButton(\"Edit\", ...));\n        toolbar->addElement(newButton(\"View\", ...));\n\n        addEntity(toolbar);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#see-also","title":"See Also","text":"
    • UILayout - Base layout class (abstract)
    • UIVerticalLayout - Vertical layout
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/padding_container/","title":"UIPaddingContainer","text":"

    Container that wraps a single UI element and applies padding.

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#description","title":"Description","text":"

    UIPaddingContainer adds padding/margin around a single child element without organizing multiple elements. Useful for adding spacing to individual elements or nesting layouts with custom padding.

    This container is simpler than UIPanel (no background/border) and focuses only on spacing.

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIPaddingContainer : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    • Inherited by: Your custom padding containers (if needed)
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/padding_container/#uipaddingcontainerfloat-x-float-y-float-w-float-h","title":"UIPaddingContainer(float x, float y, float w, float h)","text":"

    Constructs a new UIPaddingContainer.

    Parameters: - x (float): X position of the container - y (float): Y position of the container - w (float): Width of the container - h (float): Height of the container

    Example:

    #include \"graphics/ui/UIPaddingContainer.h\"\n\n// Create padding container\npixelroot32::graphics::ui::UIPaddingContainer* padded = \n    new pixelroot32::graphics::ui::UIPaddingContainer(\n        10.0f, 10.0f,\n        108.0f, 108.0f\n    );\npadded->setPadding(8.0f);  // 8px padding on all sides\n

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/padding_container/#void-setchilduielement-element","title":"void setChild(UIElement* element)","text":"

    Sets the child element.

    Parameters: - element (UIElement*): Pointer to the UI element to wrap

    Returns: - void

    Notes: - Child element is positioned with padding applied - Can wrap any UI element (button, label, layout, etc.)

    Example:

    padded->setChild(button);\n

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#uielement-getchild-const","title":"UIElement* getChild() const","text":"

    Gets the child element.

    Returns: - UIElement*: Pointer to the child element, or nullptr if none set

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#void-setpaddingfloat-p","title":"void setPadding(float p)","text":"

    Sets uniform padding on all sides.

    Parameters: - p (float): Padding value in pixels

    Returns: - void

    Notes: - Applies same padding to all sides - Child position is automatically updated

    Example:

    padded->setPadding(10.0f);  // 10px padding all around\n

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#void-setpaddingfloat-left-float-right-float-top-float-bottom","title":"void setPadding(float left, float right, float top, float bottom)","text":"

    Sets asymmetric padding.

    Parameters: - left (float): Left padding in pixels - right (float): Right padding in pixels - top (float): Top padding in pixels - bottom (float): Bottom padding in pixels

    Returns: - void

    Example:

    padded->setPadding(10.0f, 5.0f, 8.0f, 12.0f);  // Different padding per side\n

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#float-getpaddingleft-const","title":"float getPaddingLeft() const","text":"

    Gets the left padding.

    Returns: - float: Left padding in pixels

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#float-getpaddingright-const","title":"float getPaddingRight() const","text":"

    Gets the right padding.

    Returns: - float: Right padding in pixels

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#float-getpaddingtop-const","title":"float getPaddingTop() const","text":"

    Gets the top padding.

    Returns: - float: Top padding in pixels

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#float-getpaddingbottom-const","title":"float getPaddingBottom() const","text":"

    Gets the bottom padding.

    Returns: - float: Bottom padding in pixels

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#void-setpositionfloat-newx-float-newy","title":"void setPosition(float newX, float newY)","text":"

    Sets the position of the container. Also updates the child element's position.

    Parameters: - newX (float): New X coordinate - newY (float): New Y coordinate

    Returns: - void

    Notes: - Child element position is updated automatically with padding applied

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the container and child element.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    Notes: - Updates child element if set - Called automatically by Scene

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws the child element.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    Notes: - Only draws child element (no background/border) - Child is drawn at padded position

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIPaddingContainer.h\"\n\nclass MenuScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Create button\n        auto* button = new UIButton(\"Start\", 0, 0, 0, 100.0f, 30.0f, \n                                    [this]() { startGame(); });\n\n        // Wrap button with padding\n        auto* paddedButton = new pixelroot32::graphics::ui::UIPaddingContainer(\n            64.0f, 50.0f,  // position\n            120.0f, 50.0f  // size (button + padding)\n        );\n        paddedButton->setPadding(10.0f);  // 10px padding\n        paddedButton->setChild(button);\n\n        addEntity(paddedButton);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#nesting-with-layouts","title":"Nesting with Layouts","text":"
    // Create layout\nauto* layout = new UIVerticalLayout(10, 10, 108, 108);\n\n// Wrap layout with padding\nauto* paddedLayout = new UIPaddingContainer(0, 0, 128, 128);\npaddedLayout->setPadding(10.0f, 10.0f, 20.0f, 20.0f);  // Asymmetric\npaddedLayout->setChild(layout);\n
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#performance-considerations","title":"Performance Considerations","text":"
    • Rendering: Very efficient (just draws child)
    • Position calculation: Fast (simple addition)
    • Memory: Minimal overhead
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Very lightweight
    • Update frequency: Position only recalculates when padding/position changes
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#see-also","title":"See Also","text":"
    • UIElement - Base UI element class
    • UIPanel - Panel with background and border
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/panel/","title":"UIPanel","text":"

    Visual container that draws a background and border around a child element.

    "},{"location":"api_reference/ui/ui_layouts/panel/#description","title":"Description","text":"

    UIPanel provides a retro-style window/panel appearance with a background color and border. Typically contains a UILayout or other UI elements. Useful for dialogs, menus, and information panels.

    The panel wraps a single child element and draws a background rectangle and border around it.

    "},{"location":"api_reference/ui/ui_layouts/panel/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIPanel : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/panel/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    • Inherited by: Your custom panel classes (if needed)
    "},{"location":"api_reference/ui/ui_layouts/panel/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/panel/#uipanelfloat-x-float-y-float-w-float-h","title":"UIPanel(float x, float y, float w, float h)","text":"

    Constructs a new UIPanel.

    Parameters: - x (float): X position of the panel - y (float): Y position of the panel - w (float): Width of the panel - h (float): Height of the panel

    Example:

    #include \"graphics/ui/UIPanel.h\"\n\n// Create dialog panel\npixelroot32::graphics::ui::UIPanel* dialog = \n    new pixelroot32::graphics::ui::UIPanel(\n        20.0f, 30.0f,  // position\n        88.0f, 68.0f   // size\n    );\n

    "},{"location":"api_reference/ui/ui_layouts/panel/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/panel/#void-setchilduielement-element","title":"void setChild(UIElement* element)","text":"

    Sets the child element.

    Parameters: - element (UIElement*): Pointer to the UI element to wrap (typically a UILayout)

    Returns: - void

    Notes: - Child element is positioned inside the panel (with padding) - Typically a layout (VerticalLayout, etc.)

    Example:

    // Create panel\nauto* panel = new UIPanel(20, 30, 88, 68);\n\n// Create layout for panel content\nauto* layout = new UIVerticalLayout(0, 0, 80, 60);\nlayout->addElement(button1);\nlayout->addElement(button2);\n\n// Set layout as panel child\npanel->setChild(layout);\n

    "},{"location":"api_reference/ui/ui_layouts/panel/#uielement-getchild-const","title":"UIElement* getChild() const","text":"

    Gets the child element.

    Returns: - UIElement*: Pointer to the child element, or nullptr if none set

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-setbackgroundcolorcolor-color","title":"void setBackgroundColor(Color color)","text":"

    Sets the background color.

    Parameters: - color (Color): Background color

    Returns: - void

    Example:

    panel->setBackgroundColor(Color::Blue);\n

    "},{"location":"api_reference/ui/ui_layouts/panel/#color-getbackgroundcolor-const","title":"Color getBackgroundColor() const","text":"

    Gets the background color.

    Returns: - Color: Background color

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-setbordercolorcolor-color","title":"void setBorderColor(Color color)","text":"

    Sets the border color.

    Parameters: - color (Color): Border color

    Returns: - void

    Example:

    panel->setBorderColor(Color::White);\n

    "},{"location":"api_reference/ui/ui_layouts/panel/#color-getbordercolor-const","title":"Color getBorderColor() const","text":"

    Gets the border color.

    Returns: - Color: Border color

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-setborderwidthuint8_t-width","title":"void setBorderWidth(uint8_t width)","text":"

    Sets the border width.

    Parameters: - width (uint8_t): Border width in pixels

    Returns: - void

    Notes: - Default: 1 pixel - Higher values = thicker border

    Example:

    panel->setBorderWidth(2);  // 2 pixel border\n

    "},{"location":"api_reference/ui/ui_layouts/panel/#uint8_t-getborderwidth-const","title":"uint8_t getBorderWidth() const","text":"

    Gets the border width.

    Returns: - uint8_t: Border width in pixels

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-setpositionfloat-newx-float-newy","title":"void setPosition(float newX, float newY)","text":"

    Sets the position of the panel. Also updates the child element's position.

    Parameters: - newX (float): New X coordinate - newY (float): New Y coordinate

    Returns: - void

    Notes: - Child element position is updated automatically

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the panel and child element.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    Notes: - Updates child element if set - Called automatically by Scene

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws the panel (background, border) and child element.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    Notes: - Draws background rectangle - Draws border rectangle - Draws child element if set

    "},{"location":"api_reference/ui/ui_layouts/panel/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIPanel.h\"\n#include \"graphics/ui/UIVerticalLayout.h\"\n\nclass DialogScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIPanel* dialog;\n\npublic:\n    void init() override {\n        // Create dialog panel\n        dialog = new pixelroot32::graphics::ui::UIPanel(\n            20.0f, 30.0f,  // position\n            88.0f, 68.0f   // size\n        );\n        dialog->setBackgroundColor(Color::Navy);\n        dialog->setBorderColor(Color::White);\n        dialog->setBorderWidth(2);\n\n        // Create layout for dialog content\n        auto* layout = new pixelroot32::graphics::ui::UIVerticalLayout(\n            4.0f, 4.0f,  // Position inside panel\n            80.0f, 60.0f // Size inside panel\n        );\n        layout->setSpacing(8.0f);\n\n        // Add buttons\n        auto* okButton = new UIButton(\"OK\", 0, 0, 0, 70.0f, 20.0f, \n                                     [this]() { closeDialog(); });\n        auto* cancelButton = new UIButton(\"Cancel\", 1, 0, 0, 70.0f, 20.0f,\n                                         [this]() { closeDialog(); });\n\n        layout->addElement(okButton);\n        layout->addElement(cancelButton);\n\n        // Set layout as panel child\n        dialog->setChild(layout);\n\n        addEntity(dialog);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/panel/#performance-considerations","title":"Performance Considerations","text":"
    • Rendering: Simple rectangles; very efficient
    • Child updates: Child element updates are fast
    • Memory: Small overhead (just colors and border width)
    "},{"location":"api_reference/ui/ui_layouts/panel/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Panel is lightweight
    • Rendering: Two rectangles (background + border); minimal overhead
    "},{"location":"api_reference/ui/ui_layouts/panel/#see-also","title":"See Also","text":"
    • UIElement - Base UI element class
    • UILayouts - Layout containers to use inside panels
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/","title":"UIVerticalLayout","text":"

    Vertical layout container with scroll support.

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#description","title":"Description","text":"

    UIVerticalLayout organizes UI elements vertically, one below another. It supports scrolling when content exceeds the visible viewport and handles keyboard/D-pad navigation automatically.

    This layout is ideal for menus, lists, and any vertical arrangement of UI elements.

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIVerticalLayout : public UILayout {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#inheritance","title":"Inheritance","text":"
    • Inherits from: UILayout
    • Inherited by: Your custom vertical layouts (if needed)
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/vertical_layout/#uiverticallayoutfloat-x-float-y-float-w-float-h","title":"UIVerticalLayout(float x, float y, float w, float h)","text":"

    Constructs a new UIVerticalLayout.

    Parameters: - x (float): X position of the layout container - y (float): Y position of the layout container - w (float): Width of the layout container - h (float): Height of the layout container (viewport height)

    Example:

    #include \"graphics/ui/UIVerticalLayout.h\"\n\n// Create vertical layout for menu\npixelroot32::graphics::ui::UIVerticalLayout* menuLayout = \n    new pixelroot32::graphics::ui::UIVerticalLayout(\n        20.0f, 20.0f,  // position\n        88.0f, 88.0f   // size (viewport)\n    );\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-addelementuielement-element","title":"void addElement(UIElement* element)","text":"

    Adds a UI element to the layout.

    Parameters: - element (UIElement*): Pointer to the element to add

    Returns: - void

    Notes: - Elements are arranged vertically, one below another - Layout is automatically recalculated - Elements are positioned based on spacing and padding

    Example:

    menuLayout->addElement(startButton);\nmenuLayout->addElement(quitButton);\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-removeelementuielement-element","title":"void removeElement(UIElement* element)","text":"

    Removes a UI element from the layout.

    Parameters: - element (UIElement*): Pointer to the element to remove

    Returns: - void

    Notes: - Layout is automatically recalculated - Element is not deleted (you must manage its lifetime)

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-updatelayout","title":"void updateLayout()","text":"

    Recalculates positions of all elements.

    Returns: - void

    Notes: - Called automatically when elements are added/removed - Can be called manually if needed - Recalculates all element positions and content height

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-handleinputconst-inputmanager-input","title":"void handleInput(const InputManager& input)","text":"

    Handles input for navigation and scrolling.

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    Returns: - void

    Notes: - Handles UP/DOWN navigation - Manages selection state - Handles scrolling if enabled - Should be called every frame in update()

    Example:

    void update(unsigned long deltaTime) override {\n    UIVerticalLayout::update(deltaTime);\n\n    auto& input = engine.getInputManager();\n    menuLayout->handleInput(input);\n}\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the layout (handles smooth scrolling).

    Parameters: - deltaTime (unsigned long): Time elapsed since last frame in milliseconds

    Returns: - void

    Notes: - Called automatically by Scene if isEnabled is true - Updates smooth scrolling animation - Updates child elements

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws the layout and its visible elements.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    Notes: - Called automatically by Scene if isVisible is true - Only draws visible elements (viewport culling) - Draws elements in order

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setscrollenabledbool-enable","title":"void setScrollEnabled(bool enable)","text":"

    Enables or disables scrolling.

    Parameters: - enable (bool): true to enable scrolling

    Returns: - void

    Notes: - When disabled, scroll offset is reset to 0 - Scrolling is useful when content exceeds viewport height

    Example:

    menuLayout->setScrollEnabled(true);  // Enable scrolling\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-enablescrollbool-enable","title":"void enableScroll(bool enable)","text":"

    Alias for setScrollEnabled().

    Parameters: - enable (bool): true to enable scrolling

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setviewportheightfloat-h","title":"void setViewportHeight(float h)","text":"

    Sets the viewport height (visible area).

    Parameters: - h (float): Viewport height in pixels

    Returns: - void

    Notes: - Layout is automatically recalculated - Use to adjust visible area

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#float-getscrolloffset-const","title":"float getScrollOffset() const","text":"

    Gets the current scroll offset.

    Returns: - float: Scroll offset in pixels

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setscrolloffsetfloat-offset","title":"void setScrollOffset(float offset)","text":"

    Sets the scroll offset directly.

    Parameters: - offset (float): Scroll offset in pixels

    Returns: - void

    Notes: - Offset is clamped to valid range automatically - Use for programmatic scrolling

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#float-getcontentheight-const","title":"float getContentHeight() const","text":"

    Gets the total content height.

    Returns: - float: Content height in pixels

    Notes: - Includes all elements plus spacing and padding - Useful for scroll calculations

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#int-getselectedindex-const","title":"int getSelectedIndex() const","text":"

    Gets the currently selected element index.

    Returns: - int: Selected index, or -1 if none selected

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setselectedindexint-index","title":"void setSelectedIndex(int index)","text":"

    Sets the selected element index.

    Parameters: - index (int): Index to select (-1 to deselect)

    Returns: - void

    Notes: - Selected element is highlighted - Selection is scrolled into view if needed

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#uielement-getselectedelement-const","title":"UIElement* getSelectedElement() const","text":"

    Gets the selected element.

    Returns: - UIElement*: Pointer to selected element, or nullptr if none selected

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setscrollspeedfloat-speed","title":"void setScrollSpeed(float speed)","text":"

    Sets the scroll speed for smooth scrolling.

    Parameters: - speed (float): Pixels per millisecond

    Returns: - void

    Notes: - Default: 0.5 pixels per millisecond - Higher values = faster scrolling

    Example:

    menuLayout->setScrollSpeed(1.0f);  // Faster scrolling\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setnavigationbuttonsuint8_t-upbutton-uint8_t-downbutton","title":"void setNavigationButtons(uint8_t upButton, uint8_t downButton)","text":"

    Sets the navigation button indices.

    Parameters: - upButton (uint8_t): Button index for UP navigation - downButton (uint8_t): Button index for DOWN navigation

    Returns: - void

    Notes: - Default: UP = 0, DOWN = 1 - Change if your input mapping differs

    Example:

    menuLayout->setNavigationButtons(0, 1);  // UP=0, DOWN=1\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setbuttonstylecolor-selectedtextcol-color-selectedbgcol-color-unselectedtextcol-color-unselectedbgcol","title":"void setButtonStyle(Color selectedTextCol, Color selectedBgCol, Color unselectedTextCol, Color unselectedBgCol)","text":"

    Sets the style colors for selected and unselected buttons.

    Parameters: - selectedTextCol (Color): Text color when selected - selectedBgCol (Color): Background color when selected - unselectedTextCol (Color): Text color when not selected - unselectedBgCol (Color): Background color when not selected

    Returns: - void

    Example:

    menuLayout->setButtonStyle(\n    Color::Yellow,  // Selected text\n    Color::Blue,    // Selected background\n    Color::White,   // Unselected text\n    Color::Black    // Unselected background\n);\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIVerticalLayout.h\"\n#include \"graphics/ui/UIButton.h\"\n\nclass MainMenuScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIVerticalLayout* menuLayout;\n\npublic:\n    void init() override {\n        // Create menu layout\n        menuLayout = new pixelroot32::graphics::ui::UIVerticalLayout(\n            20.0f, 20.0f,  // position\n            88.0f, 88.0f   // size\n        );\n        menuLayout->setScrollEnabled(true);\n        menuLayout->setSpacing(8.0f);\n        menuLayout->setPadding(4.0f);\n\n        // Create buttons\n        auto* startButton = new pixelroot32::graphics::ui::UIButton(\n            \"Start\",\n            0, 64.0f, 50.0f, 100.0f, 30.0f,\n            [this]() { engine.setScene(&gameScene); }\n        );\n\n        auto* quitButton = new pixelroot32::graphics::ui::UIButton(\n            \"Quit\",\n            1, 64.0f, 50.0f, 100.0f, 30.0f,\n            [this]() { engine.stop(); }\n        );\n\n        // Add buttons to layout\n        menuLayout->addElement(startButton);\n        menuLayout->addElement(quitButton);\n\n        // Add layout to scene\n        addEntity(menuLayout);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Layout handles input automatically\n        auto& input = engine.getInputManager();\n        menuLayout->handleInput(input);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(0, 0, 128, 128, Color::Black);\n        Scene::draw(renderer);  // Draws layout and buttons\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#navigation","title":"Navigation","text":"

    The layout handles D-pad navigation automatically:

    • UP button: Moves selection up
    • DOWN button: Moves selection down
    • Action button: Triggers selected button's callback
    • Scrolling: Automatically scrolls to keep selected element visible
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#performance-considerations","title":"Performance Considerations","text":"
    • Viewport culling: Only visible elements are drawn
    • Layout recalculation: Fast (simple positioning)
    • Scrolling: Smooth scrolling is efficient
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Element count: Stay within MAX_ENTITIES limit
    • Scrolling: Smooth scrolling uses minimal CPU
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#see-also","title":"See Also","text":"
    • UILayout - Base layout class (abstract)
    • UIButton - Buttons for menus
    • Manual - User Interface
    • API Overview
    "},{"location":"getting_started/fundamental_concepts/","title":"Fundamental Concepts","text":"

    Before you start programming, it's important to understand the basic concepts that form PixelRoot32's architecture. This section explains how the engine works at a conceptual level, without going into code details.

    "},{"location":"getting_started/fundamental_concepts/#engine-architecture","title":"Engine Architecture","text":""},{"location":"getting_started/fundamental_concepts/#engine-the-heart-of-the-engine","title":"Engine: The Heart of the Engine","text":"

    The Engine is the main class that orchestrates the entire system. Think of it as the conductor that coordinates all subsystems:

    • Renderer: Handles drawing everything on screen
    • InputManager: Reads and processes user input (buttons, keyboard)
    • AudioEngine: Generates and plays sounds and music
    • SceneManager: Manages game scenes (menus, levels, etc.)

    The Engine runs the main game loop: an infinite cycle that updates game logic and draws each frame on screen. It also calculates delta time (time elapsed between frames) so the game runs at the same speed regardless of framerate.

    "},{"location":"getting_started/fundamental_concepts/#scene-organizing-your-game","title":"Scene: Organizing Your Game","text":"

    A Scene represents a screen or level in your game. For example: - A scene for the main menu - A scene for each game level - A scene for the game over screen - A scene for the pause menu

    Each scene contains and manages a set of entities (characters, enemies, objects, etc.). The scene is responsible for: - Initializing its entities when loaded - Updating the logic of all its entities each frame - Drawing all its visible entities each frame - Managing collisions between entities that can collide

    The Engine can only have one active scene at a time, but you can easily switch between scenes (for example, go from menu to game, or from game to pause menu).

    "},{"location":"getting_started/fundamental_concepts/#entity-the-fundamental-building-blocks","title":"Entity: The Fundamental Building Blocks","text":"

    An Entity is any object in your game that has: - Position (x, y) in the world - Size (width and height) - Visibility (can be visible or not) - Active state (can be enabled or disabled) - Render layer (in what order it's drawn)

    Entities are the foundation of everything in your game: the player, enemies, projectiles, objects, UI elements\u2014everything is an entity or inherits from Entity.

    Each entity has two main methods: - update(): Called each frame to update the entity's logic (movement, animation, etc.) - draw(): Called each frame to draw the entity on screen

    "},{"location":"getting_started/fundamental_concepts/#actor-entities-that-can-collide","title":"Actor: Entities That Can Collide","text":"

    An Actor is a special entity that can participate in the collision system. In addition to everything an Entity has, an Actor has: - Collision layer: Which group it belongs to (e.g., \"player\", \"enemy\", \"projectile\") - Collision mask: Which other groups it can collide with - Hitbox: The shape used to detect collisions (usually a rectangle)

    For example, a player might be on the \"player\" layer and have a mask that allows it to collide with \"enemies\" and \"obstacles\", but not with \"other players\".

    When two actors collide, the system calls their onCollision() method so they can react (e.g., player loses health, enemy is destroyed, etc.).

    "},{"location":"getting_started/fundamental_concepts/#physicsactor-entities-with-physics","title":"PhysicsActor: Entities with Physics","text":"

    A PhysicsActor is an Actor that also has physical properties: - Velocity (vx, vy): Moves automatically according to its velocity - Gravity: Can fall automatically - Friction: Gradually loses velocity - Restitution: Bounces when it collides (like a ball)

    The PhysicsActor updates automatically each frame, applying physics and moving the entity. It can also detect collisions with world boundaries (the walls of the play area).

    "},{"location":"getting_started/fundamental_concepts/#entity-hierarchy","title":"Entity Hierarchy","text":"

    The relationship between these classes is hierarchical:

    Entity (base)\n  \u2514\u2500\u2500 Actor (can collide)\n       \u2514\u2500\u2500 PhysicsActor (has physics)\n

    This means: - Every Actor is also an Entity - Every PhysicsActor is also an Actor and an Entity - You can use Entity for simple objects that don't need collisions - You can use Actor for objects that need to detect collisions - You can use PhysicsActor for objects that need automatic physics

    "},{"location":"getting_started/fundamental_concepts/#rendering-system","title":"Rendering System","text":""},{"location":"getting_started/fundamental_concepts/#render-layers","title":"Render Layers","text":"

    To control the order in which things are drawn, PixelRoot32 uses render layers:

    • Layer 0 (Background): Backgrounds, tilemaps, background elements
    • Layer 1 (Gameplay): Characters, enemies, projectiles, game objects
    • Layer 2 (UI): Menus, HUD, text, interface elements

    Layers are drawn in order: first 0, then 1, and finally 2. This ensures the background is always behind, gameplay in the middle, and UI always visible in front.

    Each entity has a renderLayer property that indicates which layer it should be drawn on. You can change this property to move entities between layers.

    "},{"location":"getting_started/fundamental_concepts/#rendering-pipeline","title":"Rendering Pipeline","text":"

    The rendering process works like this:

    1. beginFrame(): The screen is cleared (painted black or background color)
    2. Draw entities: All visible entities are traversed, organized by layer
    3. endFrame(): The complete frame is sent to the display

    The Renderer abstracts hardware details, so the same code works on both ESP32 (TFT_eSPI) and PC (SDL2).

    "},{"location":"getting_started/fundamental_concepts/#coordinates-and-space","title":"Coordinates and Space","text":"

    PixelRoot32 uses a standard coordinate system: - Origin (0, 0): Top-left corner - X-axis: Increases to the right - Y-axis: Increases downward

    Coordinates are in pixels. If your display is 240x240, coordinates range from (0, 0) to (239, 239).

    "},{"location":"getting_started/fundamental_concepts/#lifecycle","title":"Lifecycle","text":""},{"location":"getting_started/fundamental_concepts/#initialization","title":"Initialization","text":"

    When your game starts:

    1. Configuration: Configuration objects are created (DisplayConfig, InputConfig, AudioConfig)
    2. Engine: The Engine is created with these configurations
    3. init(): engine.init() is called to initialize all subsystems
    4. Scene: The initial scene is created and configured
    5. setScene(): The scene is assigned to the Engine
    "},{"location":"getting_started/fundamental_concepts/#game-loop","title":"Game Loop","text":"

    Once initialized, the Engine enters the game loop:

    While the game is running:\n  1. Calculate deltaTime (time since last frame)\n  2. Update InputManager (read buttons/keyboard)\n  3. Update AudioEngine (advance sounds and music)\n  4. Update current scene (update all entities)\n  5. Detect collisions in the scene\n  6. Draw the scene (draw all visible entities)\n  7. Repeat\n

    This cycle runs continuously, typically at 30-60 FPS on ESP32, or faster on PC.

    "},{"location":"getting_started/fundamental_concepts/#update","title":"Update","text":"

    Each frame, all enabled entities receive a call to their update(deltaTime) method. This is where: - Entities move - Animations update - Game logic is processed - User input is read - Sound effects are played

    The deltaTime is passed in milliseconds and represents how much time has passed since the last frame. This allows movement to be framerate-independent.

    "},{"location":"getting_started/fundamental_concepts/#rendering-draw","title":"Rendering (Draw)","text":"

    After updating, all visible entities receive a call to their draw(renderer) method. This is where: - Sprites are drawn - Text is drawn - Primitives are drawn (rectangles, circles, etc.)

    The renderer is passed as a parameter so entities can draw themselves.

    "},{"location":"getting_started/fundamental_concepts/#cleanup","title":"Cleanup","text":"

    When you change scenes or end the game: - Entities from the previous scene can be cleaned up - Resources are freed - The new scene is initialized

    "},{"location":"getting_started/fundamental_concepts/#conceptual-summary","title":"Conceptual Summary","text":"

    To summarize, PixelRoot32 works like this:

    1. Engine coordinates everything and runs the game loop
    2. Scene organizes your game into screens/levels
    3. Entity is any object in your game
    4. Actor is an entity that can collide
    5. PhysicsActor is an actor with automatic physics
    6. Renderer draws everything on screen using layers
    7. Each frame updates logic and then draws

    All of this works automatically once you configure the Engine and create your scenes and entities. You don't need to worry about game loop details; you just need to implement update() and draw() in your entities.

    "},{"location":"getting_started/fundamental_concepts/#next-step","title":"Next Step","text":"

    Now that you understand the fundamental concepts, you're ready to create your first project and see these concepts in action with real code.

    See also: - What is PixelRoot32? - Why PixelRoot32? - Your First Project - Manual - Scenes and Entities

    "},{"location":"getting_started/installation/","title":"Installation","text":"

    This guide covers installing the PixelRoot32 documentation environment and preparing your development setup for ESP32 and Native (PC) targets.

    "},{"location":"getting_started/installation/#requirements","title":"Requirements","text":"
    • Python 3.11 or newer
    • Git (recommended for source management)
    • VS Code (or your preferred IDE)
    • For ESP32 targets: PlatformIO (VS Code extension) with ESP32 toolchain
    • For Native targets: a C++ build toolchain (CMake or your OS-native toolchain)
    "},{"location":"getting_started/installation/#install-documentation-tooling","title":"Install Documentation Tooling","text":"

    To build and preview this documentation locally:

    pip install mkdocs mkdocs-material mkdocs-minify-plugin mkdocs-git-revision-date-localized-plugin mike\nmkdocs serve\n

    Open http://127.0.0.1:8000 in your browser to preview.

    "},{"location":"getting_started/installation/#esp32-setup-recommended","title":"ESP32 Setup (Recommended)","text":"
    1. Install VS Code
    2. Install PlatformIO IDE extension
    3. Install ESP32 platform/toolchain via PlatformIO
    4. Clone the engine repository:
    5. https://github.com/Gperez88/PixelRoot32-Game-Engine
    6. Open the engine or example project in VS Code (PlatformIO)
    7. Build and upload to your ESP32 board

    Tip: Use boards based on ESP32-WROOM/WROVER for best compatibility. Ensure a reliable USB cable and correct serial port selection.

    "},{"location":"getting_started/installation/#native-pc-setup","title":"Native (PC) Setup","text":"
    1. Install a C++ toolchain (e.g., MSVC or MinGW on Windows)
    2. Install CMake (if the engine provides CMake build files)
    3. Clone the engine repository:
    4. https://github.com/Gperez88/PixelRoot32-Game-Engine
    5. Configure and build the native runtime:
    6. Follow the engine\u2019s native build instructions (Development \u2192 Compiling)
    "},{"location":"getting_started/installation/#verify-your-environment","title":"Verify Your Environment","text":"
    • ESP32: Build and flash a minimal sample; confirm serial output and display if applicable
    • Native: Run the executable; confirm window output and input handling
    "},{"location":"getting_started/installation/#troubleshooting","title":"Troubleshooting","text":"
    • If PlatformIO cannot find the ESP32 platform, update PlatformIO and retry
    • If native builds fail, verify compiler versions and CMake generator settings
    • Use Community \u2192 Troubleshooting for common issues and fixes
    "},{"location":"getting_started/installation/#next-steps","title":"Next Steps","text":"
    • First Project
    • Concepts
    "},{"location":"getting_started/what_is_pixelroot32/","title":"What is PixelRoot32?","text":"

    PixelRoot32 is a lightweight, modular 2D game engine written in C++ designed specifically for ESP32 microcontrollers, with a native simulation layer for PC (SDL2) that allows you to develop and debug quickly on your desktop before deploying to hardware.

    "},{"location":"getting_started/what_is_pixelroot32/#simple-definition","title":"Simple Definition","text":"

    PixelRoot32 is a game engine that lets you create retro-style 8-bit/16-bit video games directly on an ESP32 board, with the ability to develop and test on your PC before transferring code to hardware.

    "},{"location":"getting_started/what_is_pixelroot32/#key-features","title":"Key Features","text":""},{"location":"getting_started/what_is_pixelroot32/#scene-based-architecture","title":"\ud83c\udfae Scene-Based Architecture","text":"
    • Scene system inspired by Godot Engine
    • Intuitive management of levels, menus, and screens
    • Simple transitions between scenes
    "},{"location":"getting_started/what_is_pixelroot32/#optimized-rendering","title":"\ud83c\udfa8 Optimized Rendering","text":"
    • 1bpp (monochrome) sprites as the standard format
    • Support for multi-layer sprites (MultiSprite)
    • Experimental 2bpp and 4bpp formats for higher fidelity
    • Retro color palette system (NES, GameBoy, PICO-8, etc.)
    • Compact tilemaps for backgrounds and levels
    • 2D camera with dead-zone for smooth scrolling
    • Render layer system (background, gameplay, UI)
    "},{"location":"getting_started/what_is_pixelroot32/#nes-like-audio","title":"\ud83d\udd0a NES-like Audio","text":"
    • 4 audio channels (2 Pulse, 1 Triangle, 1 Noise)
    • Integrated sound effects system
    • Music player for background melodies
    • Backends for ESP32 (internal DAC or external I2S) and SDL2
    "},{"location":"getting_started/what_is_pixelroot32/#physics-and-collisions","title":"\ud83c\udfaf Physics and Collisions","text":"
    • AABB (Axis-Aligned Bounding Box) collision system
    • PhysicsActor with gravity, friction, and restitution
    • Collision layers and masks for fine control
    • World boundary collision detection
    "},{"location":"getting_started/what_is_pixelroot32/#user-interface","title":"\ud83d\udda5\ufe0f User Interface","text":"
    • Basic elements: Labels, Buttons, Panels
    • Automatic layouts: Vertical, Horizontal, Grid, Anchor
    • Integrated D-pad navigation
    • Scroll and viewport culling for long lists
    "},{"location":"getting_started/what_is_pixelroot32/#optimized-for-esp32","title":"\u26a1 Optimized for ESP32","text":"
    • Efficient memory management
    • Integrated object pooling
    • No dynamic allocations in the game loop
    • Performance optimized for limited hardware
    "},{"location":"getting_started/what_is_pixelroot32/#typical-use-cases","title":"Typical Use Cases","text":"

    PixelRoot32 is ideal for creating:

    • Arcade Games: Space Invaders, Pong, Breakout
    • Platformers: Horizontal scrolling games with simple physics
    • Puzzles: Tetris, Snake, logic games
    • Simple RPGs: Basic role-playing games with tilemaps
    • Shooters: Vertical or horizontal shooting games
    • Rapid Prototypes: Quick development of game ideas
    "},{"location":"getting_started/what_is_pixelroot32/#supported-platforms","title":"Supported Platforms","text":""},{"location":"getting_started/what_is_pixelroot32/#esp32","title":"ESP32","text":"
    • Display: TFT_eSPI (ST7735, ILI9341, ST7789, etc.)
    • Audio: Internal DAC (GPIO 25/26) or external I2S (MAX98357A, PCM5102)
    • Input: Digital buttons, D-pad
    • Hardware: Any ESP32 board (ESP32-WROOM, ESP32-WROVER, etc.)
    "},{"location":"getting_started/what_is_pixelroot32/#desktopnative-pc","title":"Desktop/Native (PC)","text":"
    • Display: SDL2 (Windows, Linux, macOS)
    • Audio: SDL2 Audio
    • Input: Keyboard, mouse
    • Usage: Development, debugging, testing

    Note: Support for u8g2 (OLEDs) is planned for the future.

    "},{"location":"getting_started/what_is_pixelroot32/#project-status","title":"Project Status","text":"

    Current Version: v0.2.0-dev

    PixelRoot32 is under active development. APIs may change and some subsystems are still experimental. Occasional changes or breaking changes are expected, especially on less-tested configurations.

    "},{"location":"getting_started/what_is_pixelroot32/#stable-features","title":"Stable Features","text":"
    • Scene and entity system
    • Basic rendering (1bpp sprites)
    • NES-like audio system
    • Basic physics and collisions
    • Basic UI system
    • ESP32 and Native support
    "},{"location":"getting_started/what_is_pixelroot32/#experimental-features","title":"Experimental Features","text":"
    • 2bpp and 4bpp sprites (require compilation flags)
    • Scene Arena (advanced memory management)
    "},{"location":"getting_started/what_is_pixelroot32/#planned-features","title":"Planned Features","text":"
    • Support for u8g2 (OLEDs)
    • Music compiler
    • Tilemap compiler
    • Save/load system
    • Spatial partitioning for collisions
    "},{"location":"getting_started/what_is_pixelroot32/#quick-comparison","title":"Quick Comparison","text":""},{"location":"getting_started/what_is_pixelroot32/#when-to-use-pixelroot32","title":"When to use PixelRoot32?","text":"

    \u2705 Use PixelRoot32 if: - You want to create retro games on ESP32 - You need a lightweight and efficient engine - You prefer a simple and clear architecture - You want to develop on PC and deploy to ESP32 - You like 8-bit/16-bit style games

    \u274c Don't use PixelRoot32 if: - You need 3D graphics - You require advanced shaders - You need complex physics (advanced physics engines) - You want to create modern AAA games - You need support for multiple mobile platforms

    "},{"location":"getting_started/what_is_pixelroot32/#next-step","title":"Next Step","text":"

    Now that you understand what PixelRoot32 is, discover why you should use it or go directly to your first project.

    See also: - Fundamental Concepts - Installation - API Reference

    "},{"location":"getting_started/why_pixelroot32/","title":"Why PixelRoot32?","text":"

    PixelRoot32 is specifically designed to solve the unique challenges of creating video games on embedded hardware like the ESP32, while maintaining the simplicity and productivity of modern development.

    "},{"location":"getting_started/why_pixelroot32/#main-advantages","title":"Main Advantages","text":""},{"location":"getting_started/why_pixelroot32/#optimized-for-esp32","title":"\ud83c\udfaf Optimized for ESP32","text":"

    Memory Efficient - 1bpp sprite system that minimizes RAM and Flash usage - Integrated object pooling to avoid memory fragmentation - Compact tilemaps that reuse sprites - No dynamic allocations in the game loop

    Performance Optimized - Rendering optimized for ESP32 limitations - Efficient render layer system - Viewport culling to reduce draw calls - Rendering pipeline designed for limited hardware

    Real Hardware - Direct support for common TFT displays (ST7735, ILI9341, ST7789) - Integrated audio (internal DAC or external I2S) - Simple pin and hardware configuration

    "},{"location":"getting_started/why_pixelroot32/#cross-platform-development","title":"\ud83d\udda5\ufe0f Cross-Platform Development","text":"

    Develop on PC, Deploy to ESP32 - Same code works on PC (SDL2) and ESP32 - Fast debugging on desktop - Testing without hardware needed - Rapid development iteration

    Visual Consistency - Native bitmap font system (pixel-perfect) - Same rendering on PC and ESP32 - Consistent color palettes - No surprises when transferring to hardware

    "},{"location":"getting_started/why_pixelroot32/#retro-palette-system","title":"\ud83c\udfa8 Retro Palette System","text":"

    Authentic Style - Predefined palettes: NES, GameBoy, GameBoy Color, PICO-8 - Dual palette mode for visual contrasts - Custom palettes for unique styles - Automatic color resolution (RGB565)

    Easy to Use - Change palette with one line of code - Consistent visualization across all sprites - No need to manually convert assets

    "},{"location":"getting_started/why_pixelroot32/#integrated-audio","title":"\ud83d\udd0a Integrated Audio","text":"

    Complete NES-like System - 4 audio channels (2 Pulse, 1 Triangle, 1 Noise) - Simple sound effects to create - Integrated music system - Backends for different hardware configurations

    No External Dependencies - Software-generated audio - No heavy audio libraries required - Full control over sound - Deterministic and predictable

    "},{"location":"getting_started/why_pixelroot32/#simple-and-clear-architecture","title":"\ud83c\udfd7\ufe0f Simple and Clear Architecture","text":"

    Easy to Understand - Intuitive scene system (inspired by Godot) - Clear hierarchy: Entity \u2192 Actor \u2192 PhysicsActor - Consistent and predictable APIs - Clean and well-organized code

    Quick to Learn - Familiar concepts for game developers - Clear documentation and complete examples - Smooth learning curve - Active community and support

    "},{"location":"getting_started/why_pixelroot32/#complete-features","title":"\ud83c\udfae Complete Features","text":"

    Everything Needed for Games - Rendering (sprites, tilemaps, primitives) - Audio (effects and music) - Physics (gravity, collisions, basic physics) - UI (layouts, buttons, navigation) - Input (buttons, keyboard) - Camera (scroll, parallax)

    No Bloat - Only the essentials, nothing more - No heavy dependencies - Small and maintainable codebase - Easy to understand and modify

    "},{"location":"getting_started/why_pixelroot32/#tools-and-ecosystem","title":"\ud83d\udee0\ufe0f Tools and Ecosystem","text":"

    Available Tools - Sprite Compiler to convert PNG to sprites - Complete game examples - Templates and starter code - Extensive documentation

    Community and Support - Active and developing project - Open source (MIT License) - Feedback and contributions welcome - Examples available

    "},{"location":"getting_started/why_pixelroot32/#comparison-with-alternatives","title":"Comparison with Alternatives","text":""},{"location":"getting_started/why_pixelroot32/#vs-full-engines-unity-godot-etc","title":"vs. Full Engines (Unity, Godot, etc.)","text":"

    PixelRoot32 Advantages: - \u2705 Much lighter (fits in ESP32) - \u2705 No unnecessary overhead - \u2705 Full control over code - \u2705 Specifically optimized for limited hardware

    Disadvantages: - \u274c Fewer advanced features - \u274c No visual editor - \u274c Fewer resources and community

    "},{"location":"getting_started/why_pixelroot32/#vs-writing-everything-from-scratch","title":"vs. Writing Everything from Scratch","text":"

    PixelRoot32 Advantages: - \u2705 Rendering system already implemented - \u2705 Integrated and working audio - \u2705 Physics and collisions ready - \u2705 Complete UI system - \u2705 Saves months of development

    Disadvantages: - \u274c Less control over internal implementation - \u274c You must learn the engine API

    "},{"location":"getting_started/why_pixelroot32/#vs-other-esp32-engines","title":"vs. Other ESP32 Engines","text":"

    PixelRoot32 Advantages: - \u2705 More modern and clear architecture - \u2705 Better documentation - \u2705 Unique palette system - \u2705 Integrated NES-like audio - \u2705 Real cross-platform development

    "},{"location":"getting_started/why_pixelroot32/#ideal-use-cases","title":"Ideal Use Cases","text":"

    PixelRoot32 is perfect for:

    1. Educational Projects
    2. Learn game development
    3. Understand engine architecture
    4. Student projects

    5. Rapid Prototypes

    6. Quickly validate game ideas
    7. Create demos and proof-of-concepts
    8. Test mechanics

    9. Retro Games

    10. 8-bit/16-bit style games
    11. Arcade games
    12. Games with retro aesthetics

    13. Hardware Projects

    14. Games on small displays
    15. DIY portable consoles
    16. Maker/retro projects

    17. C++ Learning

    18. Clean and well-structured code
    19. Good programming practices
    20. Real and functional examples
    "},{"location":"getting_started/why_pixelroot32/#limitations-to-consider","title":"Limitations to Consider","text":"

    To be honest, PixelRoot32 has limitations:

    • Limited Hardware: Designed for ESP32, not powerful PCs
    • Simple Graphics: No 3D, no advanced shaders
    • Basic Physics: Not a complete physics engine
    • Restricted Memory: MAX_ENTITIES = 32 per scene
    • In Development: Some features are experimental

    If you need advanced features or powerful hardware, consider other engines. But for retro games on ESP32, PixelRoot32 is an excellent choice.

    "},{"location":"getting_started/why_pixelroot32/#conclusion","title":"Conclusion","text":"

    PixelRoot32 combines:

    • \u2705 Simplicity of use
    • \u2705 Efficiency for limited hardware
    • \u2705 Completeness of essential features
    • \u2705 Clarity of architecture
    • \u2705 Productivity in development

    If you want to create retro games on ESP32 without the complexity of large engines, PixelRoot32 is the right choice.

    "},{"location":"getting_started/why_pixelroot32/#next-step","title":"Next Step","text":"

    Now that you understand why PixelRoot32 is a good option, learn the fundamental concepts or start directly with your first project.

    See also: - What is PixelRoot32? - Fundamental Concepts - Your First Project

    "},{"location":"getting_started/your_first_project/","title":"Your First Project","text":"

    This guide will walk you through creating and running your first PixelRoot32 project step by step. By the end, you'll have a working project that displays a simple scene on both ESP32 and PC.

    "},{"location":"getting_started/your_first_project/#prerequisites","title":"Prerequisites","text":""},{"location":"getting_started/your_first_project/#required-software","title":"Required Software","text":"
    • PlatformIO: Install the PlatformIO IDE extension in VS Code
    • Open VS Code
    • Go to Extensions (Ctrl+Shift+X)
    • Search for \"PlatformIO IDE\"
    • Install and restart VS Code

    • Python 3.8+: Required for PlatformIO (usually installed automatically)

    "},{"location":"getting_started/your_first_project/#for-esp32-development","title":"For ESP32 Development","text":"
    • ESP32 Board: Any ESP32 development board (ESP32-WROOM, ESP32-WROVER, etc.)
    • USB Cable: To connect and program your ESP32
    • TFT Display: Compatible display (ST7735, ST7789, ILI9341, etc.)
    • Buttons: 5-6 digital buttons for input (optional for first project)
    • Audio Hardware (optional): Speaker + amplifier (PAM8302A) or I2S DAC (MAX98357A)
    "},{"location":"getting_started/your_first_project/#for-native-pc-development","title":"For Native (PC) Development","text":"
    • SDL2: Development libraries
    • Windows (MSYS2): pacman -S mingw-w64-x86_64-SDL2
    • Linux: sudo apt-get install libsdl2-dev
    • macOS: brew install sdl2
    "},{"location":"getting_started/your_first_project/#step-1-create-a-new-platformio-project","title":"Step 1: Create a New PlatformIO Project","text":"
    1. Open VS Code with PlatformIO installed

    2. Create New Project:

    3. Click on the PlatformIO icon in the sidebar
    4. Click \"New Project\"
    5. Name: my-first-pixelroot32-game
    6. Board: Select \"ESP32 Dev Module\" (or your specific board)
    7. Framework: Arduino
    8. Location: Choose your workspace folder
    9. Click \"Finish\"

    10. Project Structure: Your project should now have this structure:

      my-first-pixelroot32-game/\n\u251c\u2500\u2500 .pio/\n\u251c\u2500\u2500 include/\n\u251c\u2500\u2500 lib/\n\u251c\u2500\u2500 src/\n\u2502   \u2514\u2500\u2500 main.cpp\n\u251c\u2500\u2500 test/\n\u2514\u2500\u2500 platformio.ini\n

    "},{"location":"getting_started/your_first_project/#step-2-install-pixelroot32-engine","title":"Step 2: Install PixelRoot32 Engine","text":""},{"location":"getting_started/your_first_project/#option-a-via-platformio-library-manager-recommended","title":"Option A: Via PlatformIO Library Manager (Recommended)","text":"
    1. Open platformio.ini

    2. Add the library dependency:

    [env:esp32dev]\nplatform = espressif32\nboard = esp32dev\nframework = arduino\nlib_deps = \n    gperez88/PixelRoot32-Game-Engine@0.2.0-dev\n

    \u26a0\ufe0f IMPORTANT: Use the exact version 0.2.0-dev. Do NOT use ^ or fuzzy versioning.

    1. Save the file. PlatformIO will automatically download the library.
    "},{"location":"getting_started/your_first_project/#option-b-git-submodule","title":"Option B: Git Submodule","text":"
    1. Open terminal in your project root

    2. Add as submodule:

      git submodule add https://github.com/Gperez88/PixelRoot32-Game-Engine.git lib/PixelRoot32-Game-Engine\n

    3. Update platformio.ini:

      lib_extra_dirs = lib\n

    "},{"location":"getting_started/your_first_project/#step-3-configure-hardware-esp32","title":"Step 3: Configure Hardware (ESP32)","text":""},{"location":"getting_started/your_first_project/#configure-tft_espi-display","title":"Configure TFT_eSPI Display","text":"

    Edit platformio.ini and add build flags for your display. Here are two common configurations:

    For ST7789 (240x240):

    [env:esp32dev]\nplatform = espressif32\nboard = esp32dev\nframework = arduino\nlib_deps = \n    gperez88/PixelRoot32-Game-Engine@0.2.0-dev\n    bodmer/TFT_eSPI@^2.5.43\n\nbuild_flags = \n    -D ST7789_DRIVER\n    -D TFT_WIDTH=240\n    -D TFT_HEIGHT=240\n    -D TFT_MOSI=23\n    -D TFT_SCLK=18\n    -D TFT_DC=2\n    -D TFT_RST=4\n    -D TFT_CS=-1\n    -D LOAD_GLCD\n    -D LOAD_FONT2\n    -D LOAD_FONT4\n    -D LOAD_FONT6\n    -D LOAD_FONT7\n    -D LOAD_FONT8\n    -D LOAD_GFXFF\n    -D SMOOTH_FONT\n    -D SPI_FREQUENCY=40000000\n    -D SPI_READ_FREQUENCY=20000000\n

    For ST7735 (128x128):

    build_flags = \n    -D ST7735_DRIVER\n    -D ST7735_GREENTAB3\n    -D TFT_WIDTH=128\n    -D TFT_HEIGHT=128\n    -D TFT_MOSI=23\n    -D TFT_SCLK=18\n    -D TFT_DC=2\n    -D TFT_RST=4\n    -D TFT_CS=-1\n    -D LOAD_GLCD\n    -D LOAD_FONT2\n    -D LOAD_FONT4\n    -D LOAD_FONT6\n    -D LOAD_FONT7\n    -D LOAD_FONT8\n    -D LOAD_GFXFF\n    -D SMOOTH_FONT\n    -D SPI_FREQUENCY=27000000\n    -D SPI_READ_FREQUENCY=20000000\n

    Note: Adjust the pin numbers (TFT_MOSI, TFT_SCLK, TFT_DC, TFT_RST) to match your hardware wiring.

    "},{"location":"getting_started/your_first_project/#configure-input-optional-for-first-project","title":"Configure Input (Optional for First Project)","text":"

    If you have buttons connected, note the GPIO pins. For now, we'll create a project that works without input.

    "},{"location":"getting_started/your_first_project/#configure-audio-optional-for-first-project","title":"Configure Audio (Optional for First Project)","text":"

    Audio is optional for the first project. We'll add it later.

    "},{"location":"getting_started/your_first_project/#step-4-create-your-first-scene","title":"Step 4: Create Your First Scene","text":"

    Create a new file src/MyFirstScene.h:

    #pragma once\n#include <core/Scene.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\nclass MyFirstScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Called when the scene is initialized\n        // Set up your scene here\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Called every frame\n        // Update game logic here\n        Scene::update(deltaTime); // Don't forget to call parent update!\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Called every frame to draw\n        // Draw your scene here\n\n        // Example: Draw a simple rectangle\n        renderer.drawFilledRectangle(50, 50, 100, 100, pixelroot32::graphics::Color::Blue);\n\n        // Example: Draw text\n        renderer.drawText(\"Hello PixelRoot32!\", 20, 20, pixelroot32::graphics::Color::White, 2);\n\n        // Don't forget to call parent draw to draw all entities!\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"getting_started/your_first_project/#step-5-create-main-file-esp32","title":"Step 5: Create Main File (ESP32)","text":"

    Replace the contents of src/main.cpp with:

    #include <Arduino.h>\n#include <core/Engine.h>\n#include <drivers/esp32/TFT_eSPI_Drawer.h>\n#include <drivers/esp32/ESP32_DAC_AudioBackend.h>\n#include \"MyFirstScene.h\"\n\nnamespace pr32 = pixelroot32;\n\n// Audio configuration (optional - can be omitted for first project)\nconst int DAC_PIN = 25; // GPIO 25 or 26\npr32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(DAC_PIN, 11025);\n\n// Display configuration\n// ST7789, rotation 0, 240x240 resolution\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::ST7789, \n    0,      // rotation\n    240,    // width\n    240     // height\n);\n\n// Input configuration (6 buttons: UP, DOWN, LEFT, RIGHT, A, B)\n// For now, we'll use dummy pins - you can change these later\npr32::input::InputConfig inputConfig(\n    6,      // button count\n    32,     // UP pin\n    27,     // DOWN pin\n    33,     // LEFT pin\n    14,     // RIGHT pin\n    13,     // A button pin\n    12      // B button pin\n);\n\n// Audio configuration\npr32::audio::AudioConfig audioConfig(&audioBackend, audioBackend.getSampleRate());\n\n// Create the engine\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\n// Create your scene\nMyFirstScene myScene;\n\nvoid setup() {\n    Serial.begin(115200);\n\n    // Initialize the engine\n    engine.init();\n\n    // Initialize and set the scene\n    myScene.init();\n    engine.setScene(&myScene);\n\n    Serial.println(\"PixelRoot32 initialized!\");\n}\n\nvoid loop() {\n    // Run the game loop\n    engine.run();\n}\n
    "},{"location":"getting_started/your_first_project/#step-6-create-native-version-optional","title":"Step 6: Create Native Version (Optional)","text":"

    If you want to test on PC first, create src/main_native.cpp:

    #define SDL_MAIN_HANDLED\n#include <SDL2/SDL.h>\n#include <core/Engine.h>\n#include <drivers/native/SDL2_Drawer.h>\n#include <drivers/native/SDL2_AudioBackend.h>\n#include \"MyFirstScene.h\"\n\nnamespace pr32 = pixelroot32;\n\n// Audio configuration\npr32::drivers::native::SDL2_AudioBackend audioBackend(22050, 1024);\n\n// Display configuration (NONE defaults to SDL2 on Native)\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::NONE,\n    0,      // rotation\n    240,    // width\n    240     // height\n);\n\n// Input configuration (SDL scancodes)\npr32::input::InputConfig inputConfig(\n    6,                      // button count\n    SDL_SCANCODE_UP,        // UP\n    SDL_SCANCODE_DOWN,      // DOWN\n    SDL_SCANCODE_LEFT,      // LEFT\n    SDL_SCANCODE_RIGHT,     // RIGHT\n    SDL_SCANCODE_SPACE,     // A button\n    SDL_SCANCODE_RETURN     // B button\n);\n\n// Audio configuration\npr32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n\n// Create the engine\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\n// Create your scene\nMyFirstScene myScene;\n\nint main(int argc, char* argv[]) {\n    (void)argc;\n    (void)argv;\n\n    // Initialize the engine\n    engine.init();\n\n    // Initialize and set the scene\n    myScene.init();\n    engine.setScene(&myScene);\n\n    // Run the game loop\n    engine.run();\n\n    return 0;\n}\n
    "},{"location":"getting_started/your_first_project/#configure-native-build","title":"Configure Native Build","text":"

    Add to platformio.ini:

    [env:native]\nplatform = native\nbuild_src_filter = \n    +<*>\n    -<main.cpp>\nlib_extra_dirs = lib\nbuild_flags = \n    -D PLATFORM_NATIVE\n    -Isrc\n    -Ilib/PixelRoot32-Game-Engine/include\n    -IC:/msys64/mingw64/include/SDL2    # Windows MSYS2 path - adjust for your system\n    -LC:/msys64/mingw64/lib             # Windows MSYS2 path - adjust for your system\n    -O2\n    -Wall\n    -Wextra\n    -std=c++17\n    -lSDL2\n    -mconsole\n

    Note: Adjust the SDL2 include and library paths for your system.

    "},{"location":"getting_started/your_first_project/#step-7-build-and-run","title":"Step 7: Build and Run","text":""},{"location":"getting_started/your_first_project/#for-esp32","title":"For ESP32","text":"
    1. Connect your ESP32 via USB
    2. Select the environment: Click on the PlatformIO icon \u2192 Select env:esp32dev
    3. Build: Click the checkmark icon (\u2713) or press Ctrl+Alt+B
    4. Upload: Click the arrow icon (\u2192) or press Ctrl+Alt+U
    5. Monitor: Click the plug icon to open serial monitor

    You should see \"PixelRoot32 initialized!\" in the serial monitor and your display should show a blue rectangle and text.

    "},{"location":"getting_started/your_first_project/#for-native-pc","title":"For Native (PC)","text":"
    1. Select the environment: Click on the PlatformIO icon \u2192 Select env:native
    2. Build and Run: Click the play icon (\u25b6) or press Ctrl+Alt+R

    A window should open showing your scene with a blue rectangle and \"Hello PixelRoot32!\" text.

    "},{"location":"getting_started/your_first_project/#step-8-verify-it-works","title":"Step 8: Verify It Works","text":"

    If everything is set up correctly, you should see:

    • ESP32: Display shows a blue rectangle at (50, 50) and white text \"Hello PixelRoot32!\" at (20, 20)
    • Native: Window shows the same content

    If you see this, congratulations! Your first PixelRoot32 project is working.

    "},{"location":"getting_started/your_first_project/#troubleshooting","title":"Troubleshooting","text":""},{"location":"getting_started/your_first_project/#esp32-issues","title":"ESP32 Issues","text":"

    Display is blank: - Check wiring connections - Verify pin numbers in platformio.ini match your hardware - Check SPI frequency (try lowering it) - Verify display type (ST7789 vs ST7735)

    Compilation errors: - Ensure library version is exactly 0.2.0-dev - Check that TFT_eSPI is installed - Verify all include paths are correct

    Upload fails: - Check USB cable connection - Try different USB port - Press BOOT button on ESP32 during upload - Check COM port in PlatformIO

    "},{"location":"getting_started/your_first_project/#native-issues","title":"Native Issues","text":"

    SDL2 not found: - Verify SDL2 is installed - Check include/library paths in platformio.ini - On Windows, ensure MSYS2 paths are correct

    Window doesn't open: - Check console for error messages - Verify SDL2 is properly linked - Try running from terminal to see errors

    "},{"location":"getting_started/your_first_project/#next-steps","title":"Next Steps","text":"

    Now that you have a working project, you can:

    1. Learn about Scenes and Entities: See how to create game objects
    2. Add Input: Make your scene respond to buttons
    3. Add Sprites: Draw custom graphics
    4. Add Audio: Play sounds and music

    Continue with the Development Guide to learn more.

    See also: - Fundamental Concepts - Installation - Manual - Scenes and Entities - API Reference

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/","title":"Cameras and Scrolling","text":"

    Camera2D allows you to create worlds larger than the screen by scrolling the view. This guide covers camera setup, following targets, boundaries, and parallax effects.

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#camera2d-basics","title":"Camera2D Basics","text":"

    A Camera2D defines what portion of your game world is visible on screen.

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#creating-a-camera","title":"Creating a Camera","text":"
    #include <graphics/Camera2D.h>\n\n// Create camera with viewport size\npixelroot32::graphics::Camera2D camera(240, 240); // Screen width, height\n\n// Set camera position\ncamera.setPosition(0, 0);\n\n// Apply camera to renderer (in draw method)\ncamera.apply(renderer);\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#how-it-works","title":"How It Works","text":"

    The camera translates world coordinates to screen coordinates: - Objects at world position (100, 50) with camera at (0, 0) appear at screen (100, 50) - Objects at world position (100, 50) with camera at (50, 0) appear at screen (50, 50) - The camera effectively \"moves\" the world relative to the screen

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#following-a-target","title":"Following a Target","text":"

    The most common use is following a player or other target.

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#basic-follow","title":"Basic Follow","text":"
    class GameScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    PlayerActor* player;\n\npublic:\n    void init() override {\n        int screenWidth = engine.getRenderer().getWidth();\n        int screenHeight = engine.getRenderer().getHeight();\n\n        // Create camera\n        camera = pixelroot32::graphics::Camera2D(screenWidth, screenHeight);\n\n        // Create player\n        player = new PlayerActor(500, 300); // World position\n        addEntity(player);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Make camera follow player\n        camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Apply camera before drawing\n        camera.apply(renderer);\n\n        // Now all drawing uses camera coordinates\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#dead-zone-smooth-following","title":"Dead Zone (Smooth Following)","text":"

    For smoother following, you can implement a dead zone where the camera doesn't move until the target leaves the zone:

    void update(unsigned long deltaTime) override {\n    Scene::update(deltaTime);\n\n    // Get screen center\n    int screenCenterX = engine.getRenderer().getWidth() / 2;\n    int screenCenterY = engine.getRenderer().getHeight() / 2;\n\n    // Calculate player position relative to screen center\n    float playerScreenX = player->x - camera.getX();\n    float playerScreenY = player->y - camera.getY();\n\n    // Dead zone size\n    const int DEAD_ZONE_X = 40;\n    const int DEAD_ZONE_Y = 40;\n\n    // Move camera if player leaves dead zone\n    if (playerScreenX < screenCenterX - DEAD_ZONE_X) {\n        camera.setPosition(player->x - (screenCenterX - DEAD_ZONE_X), camera.getY());\n    } else if (playerScreenX > screenCenterX + DEAD_ZONE_X) {\n        camera.setPosition(player->x - (screenCenterX + DEAD_ZONE_X), camera.getY());\n    }\n\n    if (playerScreenY < screenCenterY - DEAD_ZONE_Y) {\n        camera.setPosition(camera.getX(), player->y - (screenCenterY - DEAD_ZONE_Y));\n    } else if (playerScreenY > screenCenterY + DEAD_ZONE_Y) {\n        camera.setPosition(camera.getX(), player->y - (screenCenterY + DEAD_ZONE_Y));\n    }\n}\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#camera-boundaries","title":"Camera Boundaries","text":"

    Limit camera movement to keep it within your level bounds.

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#setting-boundaries","title":"Setting Boundaries","text":"
    void init() override {\n    // Create camera\n    camera = pixelroot32::graphics::Camera2D(240, 240);\n\n    // Set horizontal boundaries (level is 2000 pixels wide)\n    camera.setBounds(0, 2000 - 240); // minX, maxX\n\n    // Set vertical boundaries (level is 1000 pixels tall)\n    camera.setVerticalBounds(0, 1000 - 240); // minY, maxY\n}\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#example-side-scroller-with-boundaries","title":"Example: Side-Scroller with Boundaries","text":"
    class SideScrollerScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    PlayerActor* player;\n    static const int LEVEL_WIDTH = 2000;\n    static const int LEVEL_HEIGHT = 240;\n\npublic:\n    void init() override {\n        int screenWidth = engine.getRenderer().getWidth();\n        int screenHeight = engine.getRenderer().getHeight();\n\n        camera = pixelroot32::graphics::Camera2D(screenWidth, screenHeight);\n\n        // Set boundaries (camera can't go outside level)\n        camera.setBounds(0, LEVEL_WIDTH - screenWidth);\n        camera.setVerticalBounds(0, LEVEL_HEIGHT - screenHeight);\n\n        // Create player at start\n        player = new PlayerActor(100, 100);\n        addEntity(player);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Follow player horizontally\n        camera.followTarget(player->x, camera.getY());\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        camera.apply(renderer);\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#parallax-scrolling","title":"Parallax Scrolling","text":"

    Parallax creates depth by moving background layers at different speeds.

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#basic-parallax","title":"Basic Parallax","text":"
    class ParallaxBackground : public pixelroot32::core::Entity {\nprivate:\n    float parallaxSpeed; // 0.0 to 1.0 (1.0 = normal, 0.5 = half speed)\n    float baseX;\n\npublic:\n    ParallaxBackground(float speed)\n        : Entity(0, 0, 240, 240, pixelroot32::core::EntityType::GENERIC),\n          parallaxSpeed(speed), baseX(0) {\n        setRenderLayer(0);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Get camera position\n        auto& camera = getCamera(); // You'll need to pass camera reference\n\n        // Calculate parallax offset\n        baseX = camera.getX() * parallaxSpeed;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw background with parallax offset\n        renderer.drawTileMap(backgroundTileMap, \n            static_cast<int>(baseX), 0, \n            pixelroot32::graphics::Color::White);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#multiple-parallax-layers","title":"Multiple Parallax Layers","text":"
    class ParallaxScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n\n    // Parallax layers (farther = slower)\n    ParallaxLayer* farBackground;    // Speed: 0.2\n    ParallaxLayer* midBackground;      // Speed: 0.5\n    ParallaxLayer* nearBackground;     // Speed: 0.8\n    PlayerActor* player;               // Speed: 1.0 (normal)\n\npublic:\n    void init() override {\n        camera = pixelroot32::graphics::Camera2D(240, 240);\n\n        // Create parallax layers\n        farBackground = new ParallaxLayer(0.2f);  // Moves slowest\n        midBackground = new ParallaxLayer(0.5f);\n        nearBackground = new ParallaxLayer(0.8f);\n\n        addEntity(farBackground);\n        addEntity(midBackground);\n        addEntity(nearBackground);\n\n        player = new PlayerActor(100, 100);\n        addEntity(player);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Update parallax layers with camera position\n        farBackground->updateParallax(camera.getX());\n        midBackground->updateParallax(camera.getX());\n        nearBackground->updateParallax(camera.getX());\n\n        // Follow player\n        camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        camera.apply(renderer);\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#using-setdisplayoffset-for-parallax","title":"Using setDisplayOffset for Parallax","text":"

    For simpler parallax, you can use setDisplayOffset():

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Apply camera\n    camera.apply(renderer);\n\n    // Draw far background with offset (moves slower)\n    renderer.setDisplayOffset(\n        static_cast<int>(camera.getX() * 0.3f), \n        0\n    );\n    renderer.drawTileMap(farBackground, 0, 0, Color::White);\n\n    // Draw mid background\n    renderer.setDisplayOffset(\n        static_cast<int>(camera.getX() * 0.6f), \n        0\n    );\n    renderer.drawTileMap(midBackground, 0, 0, Color::White);\n\n    // Reset offset for normal drawing\n    renderer.setDisplayOffset(0, 0);\n\n    // Draw game objects (normal speed)\n    Scene::draw(renderer);\n}\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#complete-example-platformer-with-camera","title":"Complete Example: Platformer with Camera","text":"
    class PlatformerScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    PlayerActor* player;\n    static const int LEVEL_WIDTH = 3000;\n    static const int LEVEL_HEIGHT = 800;\n\npublic:\n    void init() override {\n        int screenWidth = engine.getRenderer().getWidth();\n        int screenHeight = engine.getRenderer().getHeight();\n\n        // Create camera\n        camera = pixelroot32::graphics::Camera2D(screenWidth, screenHeight);\n\n        // Set boundaries\n        camera.setBounds(0, LEVEL_WIDTH - screenWidth);\n        camera.setVerticalBounds(0, LEVEL_HEIGHT - screenHeight);\n\n        // Create player\n        player = new PlayerActor(100, 400);\n        addEntity(player);\n\n        // Create platforms, enemies, etc.\n        // ...\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Follow player with dead zone\n        int screenCenterX = engine.getRenderer().getWidth() / 2;\n        int screenCenterY = engine.getRenderer().getHeight() / 2;\n\n        float playerScreenX = player->x - camera.getX();\n        float playerScreenY = player->y - camera.getY();\n\n        const int DEAD_ZONE = 60;\n\n        // Horizontal follow\n        if (playerScreenX < screenCenterX - DEAD_ZONE) {\n            camera.setPosition(player->x - (screenCenterX - DEAD_ZONE), camera.getY());\n        } else if (playerScreenX > screenCenterX + DEAD_ZONE) {\n            camera.setPosition(player->x - (screenCenterX + DEAD_ZONE), camera.getY());\n        }\n\n        // Vertical follow (only when falling or jumping high)\n        if (playerScreenY < screenCenterY - DEAD_ZONE || \n            playerScreenY > screenCenterY + DEAD_ZONE) {\n            camera.setPosition(camera.getX(), player->y - screenCenterY);\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Apply camera\n        camera.apply(renderer);\n\n        // Draw background (parallax)\n        renderer.setDisplayOffset(\n            static_cast<int>(camera.getX() * 0.3f), \n            0\n        );\n        renderer.drawTileMap(backgroundTileMap, 0, 0, Color::DarkGray);\n        renderer.setDisplayOffset(0, 0);\n\n        // Draw game objects\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#best-practices","title":"Best Practices","text":""},{"location":"manual/advanced_graphics/cameras_and_scrolling/#camera-movement","title":"Camera Movement","text":"
    • Use dead zones: Prevents jittery camera movement
    • Smooth transitions: Consider lerping camera position for smoother movement
    • Set boundaries: Always limit camera to level bounds
    • Test on hardware: Camera performance may differ on ESP32
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#parallax","title":"Parallax","text":"
    • Layer speeds: Farther layers move slower (0.2-0.5), closer move faster (0.7-0.9)
    • Limit layers: Too many parallax layers can impact performance
    • Use tilemaps: Parallax works best with tilemaps
    • Test visually: Ensure parallax effect is noticeable but not distracting
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#performance","title":"Performance","text":"
    • Apply once: Call camera.apply() once per frame, at start of draw()
    • Cull off-screen: Don't draw entities outside camera view
    • Limit parallax layers: 2-3 layers is usually enough
    • Optimize tilemaps: Use efficient tilemap rendering
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/advanced_graphics/cameras_and_scrolling/#camera-helper-class","title":"Camera Helper Class","text":"
    class CameraController {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    float targetX, targetY;\n    float smoothSpeed = 0.1f;\n\npublic:\n    void followTarget(float x, float y) {\n        targetX = x;\n        targetY = y;\n    }\n\n    void update(unsigned long deltaTime) {\n        // Smooth camera movement\n        float currentX = camera.getX();\n        float currentY = camera.getY();\n\n        float newX = currentX + (targetX - currentX) * smoothSpeed;\n        float newY = currentY + (targetY - currentY) * smoothSpeed;\n\n        camera.setPosition(newX, newY);\n    }\n\n    void apply(pixelroot32::graphics::Renderer& renderer) {\n        camera.apply(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#viewport-culling","title":"Viewport Culling","text":"

    Only draw entities within camera view:

    bool isVisible(float x, float y, int width, int height) {\n    float cameraX = camera.getX();\n    float cameraY = camera.getY();\n    int screenWidth = engine.getRenderer().getWidth();\n    int screenHeight = engine.getRenderer().getHeight();\n\n    return !(x + width < cameraX || \n             x > cameraX + screenWidth ||\n             y + height < cameraY || \n             y > cameraY + screenHeight);\n}\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/advanced_graphics/cameras_and_scrolling/#camera-not-moving","title":"Camera Not Moving","text":"
    • Verify camera.apply() is called in draw()
    • Check followTarget() or setPosition() is called in update()
    • Ensure camera is created with correct viewport size
    • Check boundaries aren't preventing movement
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#objects-not-visible","title":"Objects Not Visible","text":"
    • Verify objects are within camera view
    • Check world coordinates vs screen coordinates
    • Ensure camera is applied before drawing
    • Verify render layers are correct
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#parallax-not-working","title":"Parallax Not Working","text":"
    • Check setDisplayOffset() is used correctly
    • Verify parallax speed values (0.0 to 1.0)
    • Ensure offset is reset after parallax layers
    • Test with different speed values
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#next-steps","title":"Next Steps","text":"

    Now that you understand cameras and scrolling, learn about: - Tilemaps - Build levels with tiles - Particles and Effects - Add visual effects - Performance Optimization - Optimize your game

    See also: - API Reference - Camera2D - Manual - Basic Rendering - Manual - Tilemaps

    "},{"location":"manual/advanced_graphics/color_palettes/","title":"Color Palettes","text":"

    PixelRoot32 uses a palette-based color system that allows you to easily change the visual style of your game. This guide covers built-in palettes, dual palette mode, and custom palettes.

    "},{"location":"manual/advanced_graphics/color_palettes/#built-in-palettes","title":"Built-in Palettes","text":"

    PixelRoot32 includes several predefined palettes inspired by classic gaming systems:

    "},{"location":"manual/advanced_graphics/color_palettes/#available-palettes","title":"Available Palettes","text":"
    #include <graphics/PaletteDefs.h>\n\nnamespace pixelroot32::graphics {\n\nenum class PaletteType {\n    PR32,    // PixelRoot32 default palette\n    NES,     // Nintendo Entertainment System\n    GB,      // GameBoy (4 shades of green)\n    GBC,     // GameBoy Color\n    PICO8    // PICO-8 palette\n};\n
    "},{"location":"manual/advanced_graphics/color_palettes/#using-built-in-palettes","title":"Using Built-in Palettes","text":"
    #include <graphics/PaletteDefs.h>\n\n// Set palette globally (legacy mode)\npixelroot32::graphics::setPalette(pixelroot32::graphics::PaletteType::NES);\n\n// All sprites will now use NES colors\nrenderer.drawSprite(MY_SPRITE, 100, 100, pixelroot32::graphics::Color::White);\n
    "},{"location":"manual/advanced_graphics/color_palettes/#palette-characteristics","title":"Palette Characteristics","text":"

    PR32 (Default) - Modern, balanced colors - Good contrast - Suitable for most games

    NES - Classic 8-bit console colors - Limited color range - Nostalgic feel

    GB (GameBoy) - 4 shades of green - Monochrome aesthetic - Classic handheld look

    GBC (GameBoy Color) - Expanded color range - More vibrant than GB - Classic portable console

    PICO8 - PICO-8 fantasy console palette - 16 carefully chosen colors - Popular for retro games

    "},{"location":"manual/advanced_graphics/color_palettes/#legacy-mode-single-global-palette","title":"Legacy Mode (Single Global Palette)","text":"

    In legacy mode, one palette is used for all sprites:

    void MyScene::init() override {\n    // Set global palette\n    pixelroot32::graphics::setPalette(pixelroot32::graphics::PaletteType::NES);\n\n    // All sprites use NES colors\n    // This is the simplest mode\n}\n

    When to use: - Simple games - Consistent visual style - Maximum compatibility

    "},{"location":"manual/advanced_graphics/color_palettes/#dual-palette-mode","title":"Dual Palette Mode","text":"

    Dual palette mode allows different palettes for background elements and sprites, creating visual contrast.

    "},{"location":"manual/advanced_graphics/color_palettes/#enabling-dual-palette-mode","title":"Enabling Dual Palette Mode","text":"
    #include <graphics/PaletteDefs.h>\n\nvoid MyScene::init() override {\n    // Enable dual palette mode\n    pixelroot32::graphics::enableDualPaletteMode();\n\n    // Set background palette\n    pixelroot32::graphics::setBackgroundPalette(\n        pixelroot32::graphics::PaletteType::GB\n    );\n\n    // Set sprite palette\n    pixelroot32::graphics::setSpritePalette(\n        pixelroot32::graphics::PaletteType::NES\n    );\n}\n
    "},{"location":"manual/advanced_graphics/color_palettes/#how-it-works","title":"How It Works","text":"
    • Background palette: Used for tilemaps, primitives, and background sprites
    • Sprite palette: Used for game objects, characters, and foreground sprites
    • Automatic context: The renderer automatically selects the correct palette based on what you're drawing
    "},{"location":"manual/advanced_graphics/color_palettes/#example-contrasting-styles","title":"Example: Contrasting Styles","text":"
    void MyScene::init() override {\n    pixelroot32::graphics::enableDualPaletteMode();\n\n    // Dark, muted background (GameBoy green)\n    pixelroot32::graphics::setBackgroundPalette(\n        pixelroot32::graphics::PaletteType::GB\n    );\n\n    // Bright, colorful sprites (NES)\n    pixelroot32::graphics::setSpritePalette(\n        pixelroot32::graphics::PaletteType::NES\n    );\n\n    // Background uses GB palette\n    renderer.drawTileMap(backgroundTileMap, 0, 0, \n        pixelroot32::graphics::Color::White);\n\n    // Sprites use NES palette\n    renderer.drawSprite(playerSprite, 100, 100, \n        pixelroot32::graphics::Color::White);\n}\n
    "},{"location":"manual/advanced_graphics/color_palettes/#when-to-use-dual-palette-mode","title":"When to Use Dual Palette Mode","text":"
    • Visual contrast: Make sprites stand out from background
    • Artistic style: Different palettes for different layers
    • Retro aesthetics: Classic console color separation
    • Performance: No performance impact, just visual variety
    "},{"location":"manual/advanced_graphics/color_palettes/#custom-palettes","title":"Custom Palettes","text":"

    Create your own color palettes for unique visual styles.

    "},{"location":"manual/advanced_graphics/color_palettes/#creating-a-custom-palette","title":"Creating a Custom Palette","text":"
    #include <graphics/PaletteDefs.h>\n#include <graphics/Color.h>\n\n// Define custom colors (RGB565 format)\nstatic const pixelroot32::graphics::Color CUSTOM_PALETTE[] = {\n    pixelroot32::graphics::Color::Black,      // 0: Transparent/background\n    pixelroot32::graphics::Color::DarkBlue,   // 1\n    pixelroot32::graphics::Color::Blue,       // 2\n    pixelroot32::graphics::Color::LightBlue, // 3\n    pixelroot32::graphics::Color::Cyan,      // 4\n    pixelroot32::graphics::Color::White,      // 5\n    // ... more colors\n};\n\n// Set custom palette\npixelroot32::graphics::setCustomPalette(\n    CUSTOM_PALETTE,\n    sizeof(CUSTOM_PALETTE) / sizeof(pixelroot32::graphics::Color)\n);\n
    "},{"location":"manual/advanced_graphics/color_palettes/#rgb565-color-format","title":"RGB565 Color Format","text":"

    Colors in PixelRoot32 use RGB565 format (16-bit):

    // RGB565: 5 bits red, 6 bits green, 5 bits blue\n// Format: RRRRR GGGGGG BBBBB\n\n// Create custom RGB565 color\nuint16_t myColor = (31 << 11) | (63 << 5) | 31; // White\nuint16_t myColor = (0 << 11) | (0 << 5) | 0;    // Black\nuint16_t myColor = (31 << 11) | (0 << 5) | 0;   // Red\n\n// Or use Color constants\npixelroot32::graphics::Color::Red\npixelroot32::graphics::Color::Green\npixelroot32::graphics::Color::Blue\n
    "},{"location":"manual/advanced_graphics/color_palettes/#helper-function-for-custom-colors","title":"Helper Function for Custom Colors","text":"
    // Create RGB565 color from RGB values (0-255)\nuint16_t rgb565(uint8_t r, uint8_t g, uint8_t b) {\n    return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);\n}\n\n// Usage\nstatic const pixelroot32::graphics::Color MY_PALETTE[] = {\n    rgb565(0, 0, 0),        // Black\n    rgb565(255, 0, 0),      // Red\n    rgb565(0, 255, 0),      // Green\n    rgb565(0, 0, 255),      // Blue\n    rgb565(255, 255, 255),  // White\n};\n
    "},{"location":"manual/advanced_graphics/color_palettes/#complete-custom-palette-example","title":"Complete Custom Palette Example","text":"
    class CustomPaletteScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Define custom palette (ocean theme)\n        static const pixelroot32::graphics::Color OCEAN_PALETTE[] = {\n            pixelroot32::graphics::Color::Black,      // 0: Deep ocean\n            pixelroot32::graphics::Color::Navy,        // 1: Dark blue\n            pixelroot32::graphics::Color::Blue,       // 2: Medium blue\n            pixelroot32::graphics::Color::Cyan,       // 3: Light blue\n            pixelroot32::graphics::Color::LightBlue, // 4: Surface\n            pixelroot32::graphics::Color::White,      // 5: Foam\n        };\n\n        // Set custom palette\n        pixelroot32::graphics::setCustomPalette(\n            OCEAN_PALETTE,\n            sizeof(OCEAN_PALETTE) / sizeof(pixelroot32::graphics::Color)\n        );\n\n        // Now all sprites use the ocean palette\n    }\n};\n
    "},{"location":"manual/advanced_graphics/color_palettes/#color-constants","title":"Color Constants","text":"

    PixelRoot32 provides predefined color constants:

    namespace pixelroot32::graphics {\n    Color::Black\n    Color::White\n    Color::Red\n    Color::Green\n    Color::Blue\n    Color::Yellow\n    Color::Cyan\n    Color::Magenta\n    Color::DarkGray\n    Color::LightGray\n    Color::Navy\n    Color::DarkGreen\n    Color::DarkRed\n    Color::Brown\n    Color::Purple\n    Color::Orange\n    Color::Pink\n    Color::Gold\n    Color::LightBlue\n    Color::LightGreen\n    Color::LightRed\n    Color::Transparent\n}\n
    "},{"location":"manual/advanced_graphics/color_palettes/#best-practices","title":"Best Practices","text":""},{"location":"manual/advanced_graphics/color_palettes/#palette-selection","title":"Palette Selection","text":"
    • Match game style: Choose palette that fits your game's theme
    • Test on hardware: Colors may look different on ESP32 display
    • Consider contrast: Ensure sprites are visible against background
    • Consistency: Stick with one palette per scene (or use dual mode)
    "},{"location":"manual/advanced_graphics/color_palettes/#dual-palette-mode_1","title":"Dual Palette Mode","text":"
    • Use sparingly: Not all games need dual palettes
    • Test combinations: Some palette combinations work better than others
    • Clear separation: Use for clear visual distinction between layers
    • Performance: No performance cost, use freely
    "},{"location":"manual/advanced_graphics/color_palettes/#custom-palettes_1","title":"Custom Palettes","text":"
    • Limit colors: Keep palette size reasonable (8-16 colors)
    • Plan ahead: Design palette before creating sprites
    • Test thoroughly: Verify colors work well together
    • Document: Comment your palette choices
    "},{"location":"manual/advanced_graphics/color_palettes/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/advanced_graphics/color_palettes/#palette-switching","title":"Palette Switching","text":"
    class GameScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Set initial palette\n        pixelroot32::graphics::setPalette(\n            pixelroot32::graphics::PaletteType::NES\n        );\n    }\n\n    void changeToNightMode() {\n        // Switch to darker palette\n        pixelroot32::graphics::setPalette(\n            pixelroot32::graphics::PaletteType::GB\n        );\n    }\n\n    void changeToDayMode() {\n        // Switch to brighter palette\n        pixelroot32::graphics::setPalette(\n            pixelroot32::graphics::PaletteType::PICO8\n        );\n    }\n};\n
    "},{"location":"manual/advanced_graphics/color_palettes/#theme-based-palettes","title":"Theme-Based Palettes","text":"
    namespace GamePalettes {\n    // Forest theme\n    static const pixelroot32::graphics::Color FOREST[] = {\n        Color::Black,\n        Color::DarkGreen,\n        Color::Green,\n        Color::LightGreen,\n        Color::Brown,\n        Color::Yellow\n    };\n\n    // Desert theme\n    static const pixelroot32::graphics::Color DESERT[] = {\n        Color::Black,\n        Color::Brown,\n        Color::Yellow,\n        Color::Gold,\n        Color::Orange,\n        Color::White\n    };\n\n    // Ocean theme\n    static const pixelroot32::graphics::Color OCEAN[] = {\n        Color::Black,\n        Color::Navy,\n        Color::Blue,\n        Color::Cyan,\n        Color::LightBlue,\n        Color::White\n    };\n}\n
    "},{"location":"manual/advanced_graphics/color_palettes/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/advanced_graphics/color_palettes/#colors-not-changing","title":"Colors Not Changing","text":"
    • Verify setPalette() is called before drawing
    • Check palette is set in init(), not update()
    • Ensure dual palette mode is enabled if using separate palettes
    • Verify Color constants are from correct namespace
    "},{"location":"manual/advanced_graphics/color_palettes/#colors-look-wrong-on-hardware","title":"Colors Look Wrong on Hardware","text":"
    • ESP32 displays may render colors differently
    • Test on actual hardware, not just PC
    • Adjust palette colors if needed
    • Consider display calibration
    "},{"location":"manual/advanced_graphics/color_palettes/#dual-palette-not-working","title":"Dual Palette Not Working","text":"
    • Ensure enableDualPaletteMode() is called first
    • Verify both palettes are set
    • Check that you're drawing in correct context
    • Review renderer documentation
    "},{"location":"manual/advanced_graphics/color_palettes/#next-steps","title":"Next Steps","text":"

    Now that you understand palettes, learn about: - Cameras and Scrolling - Create scrolling levels - Tilemaps - Build levels with tiles - Particles and Effects - Add visual effects

    See also: - API Reference - PaletteDefs - API Reference - Color - Manual - Basic Rendering

    "},{"location":"manual/advanced_graphics/particles_and_effects/","title":"Particles and Effects","text":"

    The particle system allows you to create visual effects like fire, explosions, smoke, and sparks. This guide covers ParticleEmitter, ParticleConfig, and the included presets.

    "},{"location":"manual/advanced_graphics/particles_and_effects/#particleemitter-basics","title":"ParticleEmitter Basics","text":"

    A ParticleEmitter is an Entity that manages a pool of particles to create visual effects.

    "},{"location":"manual/advanced_graphics/particles_and_effects/#creating-a-particle-emitter","title":"Creating a Particle Emitter","text":"
    #include <graphics/particles/ParticleEmitter.h>\n#include <graphics/particles/ParticleConfig.h>\n\n// Create particle configuration\npixelroot32::graphics::particles::ParticleConfig config;\nconfig.startColor = pixelroot32::graphics::Color::Red;\nconfig.endColor = pixelroot32::graphics::Color::Yellow;\nconfig.lifetime = 1.0f; // 1 second\nconfig.speed = 50.0f;\nconfig.gravity = -100.0f; // Upward (negative = up)\n\n// Create emitter\npixelroot32::graphics::particles::ParticleEmitter* emitter = \n    new pixelroot32::graphics::particles::ParticleEmitter(100, 100, config);\n\n// Add to scene\naddEntity(emitter);\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#emitting-particles","title":"Emitting Particles","text":"
    // Emit a burst of particles\nemitter->burst(100, 100, 10); // x, y, particle count\n\n// Particles will automatically update and draw\n// No additional code needed!\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#particleconfig","title":"ParticleConfig","text":"

    ParticleConfig defines how particles behave:

    #include <graphics/particles/ParticleConfig.h>\n\npixelroot32::graphics::particles::ParticleConfig config;\n\n// Colors\nconfig.startColor = pixelroot32::graphics::Color::Red;   // Color at spawn\nconfig.endColor = pixelroot32::graphics::Color::Yellow;  // Color at death\n\n// Lifetime\nconfig.lifetime = 0.5f; // Duration in seconds\n\n// Velocity\nconfig.speed = 100.0f;           // Base speed\nconfig.speedVariation = 20.0f;   // Random variation\nconfig.direction = 90.0f;        // Direction in degrees (0 = right, 90 = up)\nconfig.directionVariation = 45.0f; // Random direction spread\n\n// Physics\nconfig.gravity = 200.0f;  // Gravity force (positive = down)\nconfig.friction = 0.95f;   // Friction (0.0 to 1.0, 1.0 = no friction)\n\n// Size\nconfig.startSize = 2;     // Size at spawn (pixels)\nconfig.endSize = 1;       // Size at death\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#complete-config-example","title":"Complete Config Example","text":"
    pixelroot32::graphics::particles::ParticleConfig fireConfig;\n\n// Fire colors (red to yellow)\nfireConfig.startColor = pixelroot32::graphics::Color::Red;\nfireConfig.endColor = pixelroot32::graphics::Color::Yellow;\n\n// Short lifetime\nfireConfig.lifetime = 0.3f;\n\n// Upward movement with variation\nfireConfig.speed = 80.0f;\nfireConfig.speedVariation = 30.0f;\nfireConfig.direction = 90.0f; // Up\nfireConfig.directionVariation = 30.0f; // Spread\n\n// Upward gravity (negative)\nfireConfig.gravity = -50.0f;\n\n// Slight friction\nfireConfig.friction = 0.98f;\n\n// Size\nfireConfig.startSize = 3;\nfireConfig.endSize = 1;\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#built-in-presets","title":"Built-in Presets","text":"

    PixelRoot32 includes several particle presets for common effects:

    "},{"location":"manual/advanced_graphics/particles_and_effects/#fire","title":"Fire","text":"
    #include <graphics/particles/ParticlePresets.h>\n\n// Create fire emitter\npixelroot32::graphics::particles::ParticleEmitter* fire = \n    new pixelroot32::graphics::particles::ParticleEmitter(\n        100, 100,\n        pixelroot32::graphics::particles::ParticlePresets::Fire()\n    );\n\n// Emit continuous fire\nvoid update(unsigned long deltaTime) override {\n    fire->burst(100, 100, 2); // Emit 2 particles per frame\n    Scene::update(deltaTime);\n}\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#explosion","title":"Explosion","text":"
    // Create explosion emitter\npixelroot32::graphics::particles::ParticleEmitter* explosion = \n    new pixelroot32::graphics::particles::ParticleEmitter(\n        100, 100,\n        pixelroot32::graphics::particles::ParticlePresets::Explosion()\n    );\n\n// Emit explosion burst\nexplosion->burst(100, 100, 20); // 20 particles at once\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#sparks","title":"Sparks","text":"
    // Create sparks emitter\npixelroot32::graphics::particles::ParticleEmitter* sparks = \n    new pixelroot32::graphics::particles::ParticleEmitter(\n        100, 100,\n        pixelroot32::graphics::particles::ParticlePresets::Sparks()\n    );\n\n// Emit sparks\nsparks->burst(100, 100, 10);\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#smoke","title":"Smoke","text":"
    // Create smoke emitter\npixelroot32::graphics::particles::ParticleEmitter* smoke = \n    new pixelroot32::graphics::particles::ParticleEmitter(\n        100, 100,\n        pixelroot32::graphics::particles::ParticlePresets::Smoke()\n    );\n\n// Emit smoke\nsmoke->burst(100, 100, 3);\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#dust","title":"Dust","text":"
    // Create dust emitter\npixelroot32::graphics::particles::ParticleEmitter* dust = \n    new pixelroot32::graphics::particles::ParticleEmitter(\n        100, 100,\n        pixelroot32::graphics::particles::ParticlePresets::Dust()\n    );\n\n// Emit dust\ndust->burst(100, 100, 5);\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#complete-example-explosion-effect","title":"Complete Example: Explosion Effect","text":"
    #include <core/Scene.h>\n#include <graphics/particles/ParticleEmitter.h>\n#include <graphics/particles/ParticlePresets.h>\n\nclass ExplosionEffect : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::particles::ParticleEmitter* explosion;\n    bool active = false;\n\npublic:\n    ExplosionEffect()\n        : Entity(0, 0, 1, 1, pixelroot32::core::EntityType::GENERIC) {\n        setRenderLayer(1);\n\n        // Create explosion emitter\n        explosion = new pixelroot32::graphics::particles::ParticleEmitter(\n            0, 0,\n            pixelroot32::graphics::particles::ParticlePresets::Explosion()\n        );\n    }\n\n    void trigger(float x, float y) {\n        active = true;\n        this->x = x;\n        this->y = y;\n\n        // Emit explosion burst\n        explosion->burst(x, y, 25);\n    }\n\n    void update(unsigned long deltaTime) override {\n        explosion->update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        explosion->draw(renderer);\n    }\n};\n\n// Usage in scene\nvoid MyScene::init() override {\n    explosionEffect = new ExplosionEffect();\n    addEntity(explosionEffect);\n}\n\nvoid MyScene::update(unsigned long deltaTime) override {\n    auto& input = engine.getInputManager();\n\n    // Trigger explosion on button press\n    if (input.isButtonPressed(4)) { // Button A\n        explosionEffect->trigger(player->x, player->y);\n    }\n\n    Scene::update(deltaTime);\n}\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#continuous-effects","title":"Continuous Effects","text":"

    For continuous effects like fire or smoke:

    class FireEffect : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::particles::ParticleEmitter* fire;\n    unsigned long emitTimer = 0;\n    const unsigned long EMIT_INTERVAL_MS = 50; // Emit every 50ms\n\npublic:\n    FireEffect(float x, float y)\n        : Entity(x, y, 1, 1, pixelroot32::core::EntityType::GENERIC) {\n        setRenderLayer(1);\n\n        fire = new pixelroot32::graphics::particles::ParticleEmitter(\n            x, y,\n            pixelroot32::graphics::particles::ParticlePresets::Fire()\n        );\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Emit particles continuously\n        emitTimer += deltaTime;\n        if (emitTimer >= EMIT_INTERVAL_MS) {\n            emitTimer -= EMIT_INTERVAL_MS;\n            fire->burst(x, y, 2); // 2 particles per interval\n        }\n\n        fire->update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        fire->draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#custom-particle-effects","title":"Custom Particle Effects","text":"

    Create your own particle effects by customizing ParticleConfig:

    "},{"location":"manual/advanced_graphics/particles_and_effects/#magic-spell-effect","title":"Magic Spell Effect","text":"
    pixelroot32::graphics::particles::ParticleConfig magicConfig;\n\n// Magical colors (purple to cyan)\nmagicConfig.startColor = pixelroot32::graphics::Color::Purple;\nmagicConfig.endColor = pixelroot32::graphics::Color::Cyan;\n\n// Medium lifetime\nmagicConfig.lifetime = 0.8f;\n\n// Outward spread\nmagicConfig.speed = 60.0f;\nmagicConfig.speedVariation = 20.0f;\nmagicConfig.direction = 0.0f; // Right\nmagicConfig.directionVariation = 360.0f; // Full circle\n\n// Slight upward float\nmagicConfig.gravity = -30.0f;\n\n// Low friction (floaty)\nmagicConfig.friction = 0.92f;\n\n// Size\nmagicConfig.startSize = 2;\nmagicConfig.endSize = 1;\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#rain-effect","title":"Rain Effect","text":"
    pixelroot32::graphics::particles::ParticleConfig rainConfig;\n\n// Rain color (light blue)\nrainConfig.startColor = pixelroot32::graphics::Color::LightBlue;\nrainConfig.endColor = pixelroot32::graphics::Color::LightBlue;\n\n// Long lifetime\nrainConfig.lifetime = 2.0f;\n\n// Downward movement\nrainConfig.speed = 150.0f;\nrainConfig.speedVariation = 20.0f;\nrainConfig.direction = 270.0f; // Down\nrainConfig.directionVariation = 5.0f; // Slight angle variation\n\n// Downward gravity\nrainConfig.gravity = 200.0f;\n\n// No friction\nrainConfig.friction = 1.0f;\n\n// Small size\nrainConfig.startSize = 1;\nrainConfig.endSize = 1;\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#best-practices","title":"Best Practices","text":""},{"location":"manual/advanced_graphics/particles_and_effects/#performance","title":"Performance","text":"
    • Limit particle count: Each emitter has MAX_PARTICLES_PER_EMITTER (50)
    • Reuse emitters: Don't create new emitters every frame
    • Disable when not visible: Set isVisible = false when off-screen
    • Limit active emitters: Too many emitters can impact performance
    "},{"location":"manual/advanced_graphics/particles_and_effects/#visual-design","title":"Visual Design","text":"
    • Match game style: Particle effects should fit your game's aesthetic
    • Use appropriate colors: Match particle colors to game palette
    • Test on hardware: ESP32 may render particles differently
    • Keep it simple: Simple effects often look better than complex ones
    "},{"location":"manual/advanced_graphics/particles_and_effects/#timing","title":"Timing","text":"
    • Burst timing: Space out bursts for better visual effect
    • Continuous effects: Use timers to control emission rate
    • Lifetime: Adjust lifetime to match effect duration
    • Cleanup: Particles automatically clean up when lifetime expires
    "},{"location":"manual/advanced_graphics/particles_and_effects/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/advanced_graphics/particles_and_effects/#one-shot-effect","title":"One-Shot Effect","text":"
    class OneShotEffect : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::particles::ParticleEmitter* emitter;\n    bool hasEmitted = false;\n\npublic:\n    void trigger(float x, float y) {\n        if (!hasEmitted) {\n            emitter->burst(x, y, 20);\n            hasEmitted = true;\n        }\n    }\n\n    void reset() {\n        hasEmitted = false;\n    }\n};\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#attached-effect","title":"Attached Effect","text":"
    class AttachedParticleEffect : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::particles::ParticleEmitter* emitter;\n    pixelroot32::core::Actor* target;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        // Update emitter position to follow target\n        emitter->x = target->x;\n        emitter->y = target->y;\n\n        // Emit particles\n        emitter->burst(target->x, target->y, 1);\n\n        emitter->update(deltaTime);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/advanced_graphics/particles_and_effects/#particles-not-appearing","title":"Particles Not Appearing","text":"
    • Verify emitter is added to scene
    • Check particle config is valid
    • Ensure burst() is being called
    • Verify emitter position is on-screen
    "},{"location":"manual/advanced_graphics/particles_and_effects/#performance-issues","title":"Performance Issues","text":"
    • Reduce particle count per burst
    • Limit number of active emitters
    • Use simpler particle configs
    • Disable emitters when not visible
    "},{"location":"manual/advanced_graphics/particles_and_effects/#particles-not-moving","title":"Particles Not Moving","text":"
    • Check gravity value (positive = down, negative = up)
    • Verify speed is not 0
    • Check friction isn't too high (1.0 = no movement)
    • Ensure direction is correct (degrees: 0=right, 90=up, 180=left, 270=down)
    "},{"location":"manual/advanced_graphics/particles_and_effects/#next-steps","title":"Next Steps","text":"

    Now that you understand particles, you've completed the advanced graphics section. Continue with: - Performance Optimization - Optimize your game - Memory Management - Manage memory efficiently - API Reference - Complete API documentation

    See also: - API Reference - ParticleEmitter - API Reference - ParticleConfig - API Reference - ParticlePresets - Manual - Basic Rendering

    "},{"location":"manual/advanced_graphics/sprites_and_animation/","title":"Sprites and Animation","text":"

    This guide covers advanced sprite techniques and animation in PixelRoot32, including different sprite formats, creating animations, and best practices.

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#sprite-formats","title":"Sprite Formats","text":"

    PixelRoot32 supports multiple sprite formats, each optimized for different use cases.

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#1bpp-standard-monochrome","title":"1bpp (Standard, Monochrome)","text":"

    The standard format uses 1 bit per pixel (monochrome). This is the most memory-efficient format:

    #include <graphics/Renderer.h>\n\n// Define sprite data (8x8 example)\nstatic const uint16_t PLAYER_SPRITE_DATA[] = {\n    0b00111100,  // Row 0\n    0b01111110,  // Row 1\n    0b11111111,  // Row 2\n    0b11111111,  // Row 3\n    0b11111111,  // Row 4\n    0b01111110,  // Row 5\n    0b00111100,  // Row 6\n    0b00000000   // Row 7\n};\n\n// Create sprite descriptor\nstatic const pixelroot32::graphics::Sprite PLAYER_SPRITE = {\n    PLAYER_SPRITE_DATA,\n    8,  // width\n    8   // height\n};\n\n// Draw sprite\nrenderer.drawSprite(PLAYER_SPRITE, 100, 100, pixelroot32::graphics::Color::White);\n

    Characteristics: - Most memory-efficient - 1 bit per pixel - Maximum width: 16 pixels - Color applied at draw time - Best for: Simple graphics, retro style

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#2bpp-experimental-4-colors","title":"2bpp (Experimental, 4 Colors)","text":"

    2 bits per pixel allows 4 colors per sprite:

    #ifdef PIXELROOT32_ENABLE_2BPP_SPRITES\n#include <graphics/Renderer.h>\n\n// Define 2bpp sprite data\nstatic const uint8_t COLORFUL_SPRITE_DATA[] = {\n    // Each byte represents 4 pixels (2 bits each)\n    // Format: [pixel3][pixel2][pixel1][pixel0]\n    0x00, 0x11, 0x22, 0x33,  // Row 0\n    0x11, 0x22, 0x33, 0x00,  // Row 1\n    // ... more rows\n};\n\n// Define palette (4 colors)\nstatic const pixelroot32::graphics::Color SPRITE_PALETTE[] = {\n    pixelroot32::graphics::Color::Transparent,\n    pixelroot32::graphics::Color::Red,\n    pixelroot32::graphics::Color::Green,\n    pixelroot32::graphics::Color::Blue\n};\n\n// Create 2bpp sprite\nstatic const pixelroot32::graphics::Sprite2bpp COLORFUL_SPRITE = {\n    COLORFUL_SPRITE_DATA,\n    SPRITE_PALETTE,\n    16,  // width\n    8,   // height\n    4    // palette size\n};\n\n// Draw 2bpp sprite\nrenderer.drawSprite(COLORFUL_SPRITE, 100, 100, false);\n#endif\n

    Characteristics: - 2 bits per pixel (4 colors) - Requires custom palette - More memory than 1bpp - Best for: More colorful sprites without full color

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#4bpp-experimental-16-colors","title":"4bpp (Experimental, 16 Colors)","text":"

    4 bits per pixel allows 16 colors per sprite:

    #ifdef PIXELROOT32_ENABLE_4BPP_SPRITES\n#include <graphics/Renderer.h>\n\n// Define 4bpp sprite data\nstatic const uint8_t RICH_SPRITE_DATA[] = {\n    // Each byte represents 2 pixels (4 bits each)\n    // Format: [pixel1][pixel0]\n    0x01, 0x23, 0x45, 0x67,  // Row 0\n    // ... more rows\n};\n\n// Define palette (16 colors)\nstatic const pixelroot32::graphics::Color RICH_PALETTE[] = {\n    pixelroot32::graphics::Color::Transparent,\n    pixelroot32::graphics::Color::Black,\n    pixelroot32::graphics::Color::DarkGray,\n    // ... 13 more colors\n};\n\n// Create 4bpp sprite\nstatic const pixelroot32::graphics::Sprite4bpp RICH_SPRITE = {\n    RICH_SPRITE_DATA,\n    RICH_PALETTE,\n    16,  // width\n    16,  // height\n    16   // palette size\n};\n\n// Draw 4bpp sprite\nrenderer.drawSprite(RICH_SPRITE, 100, 100, false);\n#endif\n

    Characteristics: - 4 bits per pixel (16 colors) - Requires custom palette - Most memory-intensive - Best for: Detailed sprites with many colors

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#multisprite-multi-layer","title":"MultiSprite (Multi-Layer)","text":"

    MultiSprite combines multiple 1bpp layers to create multi-color sprites:

    #include <graphics/Renderer.h>\n\n// Define layers (each is 1bpp)\nstatic const uint16_t BASE_LAYER_DATA[] = {\n    0b00111100,\n    0b01111110,\n    0b11111111,\n    0b11111111,\n    0b11111111,\n    0b01111110,\n    0b00111100,\n    0b00000000\n};\n\nstatic const uint16_t HIGHLIGHT_LAYER_DATA[] = {\n    0b00000000,\n    0b00011000,\n    0b00111100,\n    0b00111100,\n    0b00111100,\n    0b00011000,\n    0b00000000,\n    0b00000000\n};\n\n// Create layers\nstatic const pixelroot32::graphics::SpriteLayer LAYERS[] = {\n    { BASE_LAYER_DATA, pixelroot32::graphics::Color::Blue },      // Base layer\n    { HIGHLIGHT_LAYER_DATA, pixelroot32::graphics::Color::Cyan }  // Highlight layer\n};\n\n// Create MultiSprite\nstatic const pixelroot32::graphics::MultiSprite PLAYER_MULTI = {\n    8,      // width\n    8,      // height\n    LAYERS, // layers array\n    2       // layer count\n};\n\n// Draw MultiSprite\nrenderer.drawSprite(PLAYER_MULTI, 100, 100, false);\n

    Characteristics: - Combines multiple 1bpp layers - Each layer can have different color - Layers drawn in order (first = bottom) - Best for: Complex sprites with highlights, outlines, etc.

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#creating-sprites","title":"Creating Sprites","text":""},{"location":"manual/advanced_graphics/sprites_and_animation/#manual-creation-1bpp","title":"Manual Creation (1bpp)","text":"

    For simple sprites, you can create them manually:

    // 8x8 sprite: Simple circle\nstatic const uint16_t CIRCLE_SPRITE_DATA[] = {\n    0b00111100,  //   ####\n    0b01111110,  //  ######\n    0b11111111,  // ########\n    0b11111111,  // ########\n    0b11111111,  // ########\n    0b11111111,  // ########\n    0b01111110,  //  ######\n    0b00111100   //   ####\n};\n\nstatic const pixelroot32::graphics::Sprite CIRCLE_SPRITE = {\n    CIRCLE_SPRITE_DATA,\n    8,\n    8\n};\n

    Tips: - Use binary notation for clarity - Comment each row to visualize - Keep sprites small (8x8, 16x16) - Reuse sprites when possible

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#using-sprite-compiler","title":"Using Sprite Compiler","text":"

    For complex sprites, use the Sprite Compiler tool (if available) to convert PNG images to sprite data.

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#sprite-animation","title":"Sprite Animation","text":"

    PixelRoot32 uses a step-based animation system that's lightweight and efficient.

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#spriteanimation-structure","title":"SpriteAnimation Structure","text":"
    #include <graphics/Renderer.h>\n\n// Define animation frames\nstatic const uint16_t FRAME1_DATA[] = { /* ... */ };\nstatic const uint16_t FRAME2_DATA[] = { /* ... */ };\nstatic const uint16_t FRAME3_DATA[] = { /* ... */ };\n\nstatic const pixelroot32::graphics::Sprite FRAME1 = { FRAME1_DATA, 8, 8 };\nstatic const pixelroot32::graphics::Sprite FRAME2 = { FRAME2_DATA, 8, 8 };\nstatic const pixelroot32::graphics::Sprite FRAME3 = { FRAME3_DATA, 8, 8 };\n\n// Create animation frames\nstatic const pixelroot32::graphics::SpriteAnimationFrame ANIMATION_FRAMES[] = {\n    { &FRAME1, nullptr },  // Frame 1 (no mask)\n    { &FRAME2, nullptr },  // Frame 2\n    { &FRAME3, nullptr }   // Frame 3\n};\n\n// Create animation\npixelroot32::graphics::SpriteAnimation walkAnimation;\nwalkAnimation.frames = ANIMATION_FRAMES;\nwalkAnimation.frameCount = 3;\nwalkAnimation.current = 0;\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#using-animations-in-actors","title":"Using Animations in Actors","text":"
    #include <core/Actor.h>\n#include <graphics/Renderer.h>\n\nclass AnimatedPlayer : public pixelroot32::core::Actor {\nprivate:\n    pixelroot32::graphics::SpriteAnimation walkAnimation;\n    unsigned long animationTimer = 0;\n    const unsigned long FRAME_DURATION_MS = 100; // 100ms per frame\n\npublic:\n    AnimatedPlayer(float x, float y)\n        : Actor(x, y, 8, 8) {\n        setRenderLayer(1);\n\n        // Initialize animation\n        walkAnimation.frames = WALK_ANIMATION_FRAMES;\n        walkAnimation.frameCount = 3;\n        walkAnimation.current = 0;\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Update animation\n        animationTimer += deltaTime;\n        if (animationTimer >= FRAME_DURATION_MS) {\n            animationTimer -= FRAME_DURATION_MS;\n            walkAnimation.step(); // Advance to next frame\n        }\n\n        // Movement logic...\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Get current frame\n        const pixelroot32::graphics::Sprite* currentFrame = \n            walkAnimation.frames[walkAnimation.current].sprite;\n\n        // Draw current frame\n        renderer.drawSprite(\n            *currentFrame,\n            static_cast<int>(x),\n            static_cast<int>(y),\n            pixelroot32::graphics::Color::White,\n            false // flipX\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Handle collision\n    }\n};\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#animation-control","title":"Animation Control","text":"
    // Reset animation to first frame\nwalkAnimation.reset();\n\n// Step to next frame (loops automatically)\nwalkAnimation.step();\n\n// Get current frame\nconst pixelroot32::graphics::Sprite* frame = \n    walkAnimation.frames[walkAnimation.current].sprite;\n\n// Check if animation is at specific frame\nif (walkAnimation.current == 0) {\n    // At first frame\n}\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#multiple-animations","title":"Multiple Animations","text":"

    For actors with multiple animations (idle, walk, jump):

    class PlayerActor : public pixelroot32::core::Actor {\nprivate:\n    enum class AnimationState {\n        IDLE,\n        WALK,\n        JUMP\n    };\n\n    AnimationState currentState = AnimationState::IDLE;\n    pixelroot32::graphics::SpriteAnimation idleAnim;\n    pixelroot32::graphics::SpriteAnimation walkAnim;\n    pixelroot32::graphics::SpriteAnimation jumpAnim;\n\n    pixelroot32::graphics::SpriteAnimation* getCurrentAnimation() {\n        switch (currentState) {\n            case AnimationState::IDLE: return &idleAnim;\n            case AnimationState::WALK: return &walkAnim;\n            case AnimationState::JUMP: return &jumpAnim;\n        }\n        return &idleAnim;\n    }\n\npublic:\n    void update(unsigned long deltaTime) override {\n        // Update current animation\n        auto* anim = getCurrentAnimation();\n        animationTimer += deltaTime;\n        if (animationTimer >= FRAME_DURATION_MS) {\n            animationTimer -= FRAME_DURATION_MS;\n            anim->step();\n        }\n\n        // Change animation state based on game logic\n        if (isMoving) {\n            if (currentState != AnimationState::WALK) {\n                currentState = AnimationState::WALK;\n                walkAnim.reset();\n            }\n        } else if (isJumping) {\n            if (currentState != AnimationState::JUMP) {\n                currentState = AnimationState::JUMP;\n                jumpAnim.reset();\n            }\n        } else {\n            if (currentState != AnimationState::IDLE) {\n                currentState = AnimationState::IDLE;\n                idleAnim.reset();\n            }\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        auto* anim = getCurrentAnimation();\n        const auto* frame = anim->frames[anim->current].sprite;\n        renderer.drawSprite(*frame, static_cast<int>(x), static_cast<int>(y), \n            pixelroot32::graphics::Color::White);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#sprite-flipping","title":"Sprite Flipping","text":"

    Flip sprites horizontally for facing direction:

    bool facingRight = true;\n\nvoid draw(pixelroot32::graphics::Renderer& renderer) override {\n    renderer.drawSprite(\n        PLAYER_SPRITE,\n        static_cast<int>(x),\n        static_cast<int>(y),\n        pixelroot32::graphics::Color::White,\n        !facingRight // Flip if facing left\n    );\n}\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#best-practices","title":"Best Practices","text":""},{"location":"manual/advanced_graphics/sprites_and_animation/#memory-optimization","title":"Memory Optimization","text":"
    • Reuse sprites: Define sprites once, use many times
    • Use 1bpp when possible: Most memory-efficient
    • Store in flash: Use static const to keep in flash memory
    • Limit sprite count: Too many unique sprites can exhaust memory
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#performance","title":"Performance","text":"
    • Pre-calculate animations: Set up animations in init(), not update()
    • Limit active animations: Only animate visible entities
    • Use appropriate formats: Don't use 4bpp if 1bpp works
    • Batch similar sprites: Draw similar sprites together
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#organization","title":"Organization","text":"
    • Group related sprites: Keep sprite data together
    • Use meaningful names: Name sprites clearly
    • Document complex sprites: Comment sprite bit patterns
    • Create sprite libraries: Reusable sprite collections
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/advanced_graphics/sprites_and_animation/#sprite-sheet-pattern","title":"Sprite Sheet Pattern","text":"

    Organize multiple sprites in arrays:

    namespace PlayerSprites {\n    static const uint16_t IDLE_FRAME1[] = { /* ... */ };\n    static const uint16_t IDLE_FRAME2[] = { /* ... */ };\n    static const uint16_t WALK_FRAME1[] = { /* ... */ };\n    static const uint16_t WALK_FRAME2[] = { /* ... */ };\n\n    static const pixelroot32::graphics::Sprite IDLE[] = {\n        { IDLE_FRAME1, 8, 8 },\n        { IDLE_FRAME2, 8, 8 }\n    };\n\n    static const pixelroot32::graphics::Sprite WALK[] = {\n        { WALK_FRAME1, 8, 8 },\n        { WALK_FRAME2, 8, 8 }\n    };\n}\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#animation-helper","title":"Animation Helper","text":"

    Create a helper class for animation management:

    class AnimationController {\nprivate:\n    pixelroot32::graphics::SpriteAnimation* currentAnim;\n    unsigned long timer = 0;\n    unsigned long frameDuration;\n\npublic:\n    void setAnimation(pixelroot32::graphics::SpriteAnimation* anim) {\n        if (currentAnim != anim) {\n            currentAnim = anim;\n            currentAnim->reset();\n            timer = 0;\n        }\n    }\n\n    void update(unsigned long deltaTime) {\n        if (currentAnim) {\n            timer += deltaTime;\n            if (timer >= frameDuration) {\n                timer -= frameDuration;\n                currentAnim->step();\n            }\n        }\n    }\n\n    const pixelroot32::graphics::Sprite* getCurrentFrame() {\n        if (currentAnim && currentAnim->frameCount > 0) {\n            return currentAnim->frames[currentAnim->current].sprite;\n        }\n        return nullptr;\n    }\n};\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/advanced_graphics/sprites_and_animation/#sprites-not-displaying","title":"Sprites Not Displaying","text":"
    • Check sprite data is valid
    • Verify width/height match data
    • Ensure sprite is within screen bounds
    • Check render layer is correct
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#animation-not-playing","title":"Animation Not Playing","text":"
    • Verify animation frames are set
    • Check step() is being called
    • Ensure timer logic is correct
    • Verify frame count matches array size
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#memory-issues","title":"Memory Issues","text":"
    • Reduce sprite count
    • Use 1bpp instead of 2bpp/4bpp
    • Reuse sprites more
    • Check available flash memory
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#next-steps","title":"Next Steps","text":"

    Now that you understand sprites and animation, learn about: - Color Palettes - Use different color schemes - Cameras and Scrolling - Create scrolling levels - Tilemaps - Build levels with tiles

    See also: - API Reference - Sprite - API Reference - SpriteAnimation - Manual - Basic Rendering

    "},{"location":"manual/advanced_graphics/tilemaps/","title":"Tilemaps","text":"

    Tilemaps allow you to build levels efficiently by reusing small tile sprites. This guide covers creating tilemaps, rendering them, and using them with scrolling cameras.

    "},{"location":"manual/advanced_graphics/tilemaps/#what-are-tilemaps","title":"What are Tilemaps?","text":"

    A tilemap is a 2D grid where each cell references a tile sprite. Instead of placing individual sprites, you define which tile appears at each grid position.

    Advantages: - Memory efficient: Reuse tile sprites many times - Easy level design: Edit level data, not code - Fast rendering: Optimized tilemap drawing - Large levels: Create levels bigger than screen - Multiple Bit-Depths: Support for 1bpp, 2bpp, and 4bpp tilemaps for higher graphical fidelity

    "},{"location":"manual/advanced_graphics/tilemaps/#creating-a-tilemap","title":"Creating a Tilemap","text":""},{"location":"manual/advanced_graphics/tilemaps/#1-define-tiles","title":"1. Define Tiles","text":"

    First, create the tile sprites you'll reuse. You can use standard 1bpp sprites or multi-bpp sprites (2bpp/4bpp) if enabled.

    "},{"location":"manual/advanced_graphics/tilemaps/#1bpp-tiles-example","title":"1bpp Tiles Example","text":"
    #include <graphics/Renderer.h>\n\n// Ground tile (solid)\nstatic const uint16_t TILE_GROUND_BITS[] = {\n    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,\n    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF\n};\n\n// Create tile sprites (8x8 tiles)\nstatic const pixelroot32::graphics::Sprite TILES[] = {\n    { TILE_EMPTY_BITS, 8, 8 },  // Index 0: Empty\n    { TILE_GROUND_BITS, 8, 8 }  // Index 1: Ground\n};\n
    "},{"location":"manual/advanced_graphics/tilemaps/#2bpp-tiles-example-multi-color","title":"2bpp Tiles Example (Multi-color)","text":"
    #include <graphics/Renderer.h>\n\n// 2bpp grass tile\nstatic const uint8_t TILE_GRASS_DATA[] = {\n    0b01010101, 0b01010101, // Packed 2bpp data\n    // ...\n};\n\nstatic const Color GRASS_PALETTE[] = {\n    Color::Transparent, Color::DarkGreen, Color::Green, Color::LightGreen\n};\n\nstatic const pixelroot32::graphics::Sprite2bpp TILES_2BPP[] = {\n    { TILE_GRASS_DATA, GRASS_PALETTE, 8, 8, 4 }\n};\n
    "},{"location":"manual/advanced_graphics/tilemaps/#2-create-tile-index-array","title":"2. Create Tile Index Array","text":"

    Define which tile appears at each position:

    // Tilemap dimensions (30 tiles wide, 20 tiles tall)\nstatic const int TILEMAP_WIDTH = 30;\nstatic const int TILEMAP_HEIGHT = 20;\n\n// Array of tile indices (each byte is a tile index)\nstatic uint8_t TILEMAP_INDICES[TILEMAP_WIDTH * TILEMAP_HEIGHT];\n\n// Initialize to empty\nvoid initTilemap() {\n    for (int i = 0; i < TILEMAP_WIDTH * TILEMAP_HEIGHT; i++) {\n        TILEMAP_INDICES[i] = 0; // Empty\n    }\n\n    // Draw ground at bottom\n    int groundRow = TILEMAP_HEIGHT - 1;\n    for (int x = 0; x < TILEMAP_WIDTH; x++) {\n        TILEMAP_INDICES[groundRow * TILEMAP_WIDTH + x] = 1; // Ground tile\n    }\n\n    // Add some walls\n    TILEMAP_INDICES[5 * TILEMAP_WIDTH + 10] = 2; // Wall at (10, 5)\n    TILEMAP_INDICES[5 * TILEMAP_WIDTH + 11] = 2;\n    TILEMAP_INDICES[5 * TILEMAP_WIDTH + 12] = 2;\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#3-create-tilemap-structure","title":"3. Create TileMap Structure","text":"
    #include <graphics/Renderer.h>\n\nstatic pixelroot32::graphics::TileMap myTileMap = {\n    TILEMAP_INDICES,                    // indices array\n    TILEMAP_WIDTH,                      // width (in tiles)\n    TILEMAP_HEIGHT,                     // height (in tiles)\n    TILES,                              // tiles array\n    8,                                  // tile width (pixels)\n    8,                                  // tile height (pixels)\n    sizeof(TILES) / sizeof(pixelroot32::graphics::Sprite) // tile count\n};\n
    "},{"location":"manual/advanced_graphics/tilemaps/#rendering-tilemaps","title":"Rendering Tilemaps","text":""},{"location":"manual/advanced_graphics/tilemaps/#basic-rendering","title":"Basic Rendering","text":"
    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // 1bpp Tilemap (requires a color)\n    renderer.drawTileMap(\n        myTileMap,\n        0, 0,\n        pixelroot32::graphics::Color::White\n    );\n\n    // 2bpp/4bpp Tilemap (colors are in the sprite palettes)\n    renderer.drawTileMap(myTileMap2bpp, 0, 100);\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#with-camerascrolling","title":"With Camera/Scrolling","text":"
    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Apply camera first\n    camera.apply(renderer);\n\n    // Draw tilemap (camera offset is automatically applied)\n    renderer.drawTileMap(\n        myTileMap,\n        0,                              // World position (0, 0)\n        0,\n        pixelroot32::graphics::Color::White\n    );\n\n    // Draw game objects\n    Scene::draw(renderer);\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#complete-example-platformer-level","title":"Complete Example: Platformer Level","text":"
    #include <core/Scene.h>\n#include <graphics/Renderer.h>\n#include <graphics/Camera2D.h>\n\nclass PlatformerLevel : public pixelroot32::core::Scene {\nprivate:\n    static const int TILE_SIZE = 8;\n    static const int TILEMAP_WIDTH = 100;  // 800 pixels wide\n    static const int TILEMAP_HEIGHT = 30;   // 240 pixels tall\n\n    // Tile definitions\n    static const uint16_t TILE_EMPTY_BITS[] = {\n        0x0000, 0x0000, 0x0000, 0x0000,\n        0x0000, 0x0000, 0x0000, 0x0000\n    };\n\n    static const uint16_t TILE_GROUND_BITS[] = {\n        0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,\n        0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF\n    };\n\n    static const uint16_t TILE_GRASS_BITS[] = {\n        0x0000, 0x0000, 0x0000, 0x0000,\n        0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF\n    };\n\n    static const pixelroot32::graphics::Sprite TILES[] = {\n        { TILE_EMPTY_BITS, TILE_SIZE, TILE_SIZE },  // 0: Empty\n        { TILE_GROUND_BITS, TILE_SIZE, TILE_SIZE }, // 1: Ground\n        { TILE_GRASS_BITS, TILE_SIZE, TILE_SIZE }   // 2: Grass top\n    };\n\n    static uint8_t LEVEL_INDICES[TILEMAP_WIDTH * TILEMAP_HEIGHT];\n\n    pixelroot32::graphics::TileMap levelTileMap;\n    pixelroot32::graphics::Camera2D camera;\n\npublic:\n    PlatformerLevel() \n        : camera(240, 240) {\n        // Initialize tilemap structure\n        levelTileMap = {\n            LEVEL_INDICES,\n            TILEMAP_WIDTH,\n            TILEMAP_HEIGHT,\n            TILES,\n            TILE_SIZE,\n            TILE_SIZE,\n            sizeof(TILES) / sizeof(pixelroot32::graphics::Sprite)\n        };\n    }\n\n    void init() override {\n        // Initialize all tiles to empty\n        for (int i = 0; i < TILEMAP_WIDTH * TILEMAP_HEIGHT; i++) {\n            LEVEL_INDICES[i] = 0;\n        }\n\n        // Create ground level\n        int groundY = TILEMAP_HEIGHT - 1;\n        for (int x = 0; x < TILEMAP_WIDTH; x++) {\n            LEVEL_INDICES[groundY * TILEMAP_WIDTH + x] = 1; // Ground\n        }\n\n        // Add grass on top of ground\n        int grassY = groundY - 1;\n        for (int x = 0; x < TILEMAP_WIDTH; x++) {\n            LEVEL_INDICES[grassY * TILEMAP_WIDTH + x] = 2; // Grass\n        }\n\n        // Add platforms\n        // Platform 1: x=10 to x=15, y=20\n        for (int x = 10; x < 16; x++) {\n            LEVEL_INDICES[20 * TILEMAP_WIDTH + x] = 1; // Ground tile\n        }\n\n        // Platform 2: x=30 to x=35, y=15\n        for (int x = 30; x < 36; x++) {\n            LEVEL_INDICES[15 * TILEMAP_WIDTH + x] = 1;\n        }\n\n        // Set camera boundaries\n        camera.setBounds(0, TILEMAP_WIDTH * TILE_SIZE - 240);\n        camera.setVerticalBounds(0, TILEMAP_HEIGHT * TILE_SIZE - 240);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Follow player (example)\n        // camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Apply camera\n        camera.apply(renderer);\n\n        // Draw tilemap\n        renderer.drawTileMap(\n            levelTileMap,\n            0, 0,\n            pixelroot32::graphics::Color::White\n        );\n\n        // Draw game objects\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/tilemaps/#tilemap-with-scroll","title":"Tilemap with Scroll","text":"

    For scrolling levels, combine tilemaps with cameras:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Apply camera (handles scrolling)\n    camera.apply(renderer);\n\n    // Draw tilemap (automatically scrolled by camera)\n    renderer.drawTileMap(\n        levelTileMap,\n        0, 0,\n        pixelroot32::graphics::Color::White\n    );\n\n    // Draw entities (also scrolled)\n    Scene::draw(renderer);\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#optimizing-tilemap-rendering","title":"Optimizing Tilemap Rendering","text":""},{"location":"manual/advanced_graphics/tilemaps/#viewport-culling","title":"Viewport Culling","text":"

    Only draw visible tiles:

    void drawTileMapOptimized(\n    pixelroot32::graphics::Renderer& renderer,\n    const pixelroot32::graphics::TileMap& tileMap,\n    int offsetX, int offsetY,\n    pixelroot32::graphics::Color color\n) {\n    int screenWidth = renderer.getWidth();\n    int screenHeight = renderer.getHeight();\n\n    // Calculate which tiles are visible\n    int startTileX = (offsetX < 0) ? (-offsetX / tileMap.tileWidth) : 0;\n    int startTileY = (offsetY < 0) ? (-offsetY / tileMap.tileHeight) : 0;\n    int endTileX = startTileX + (screenWidth / tileMap.tileWidth) + 1;\n    int endTileY = startTileY + (screenHeight / tileMap.tileHeight) + 1;\n\n    // Clamp to tilemap bounds\n    if (startTileX < 0) startTileX = 0;\n    if (startTileY < 0) startTileY = 0;\n    if (endTileX > tileMap.width) endTileX = tileMap.width;\n    if (endTileY > tileMap.height) endTileY = tileMap.height;\n\n    // Draw only visible tiles\n    for (int ty = startTileY; ty < endTileY; ty++) {\n        for (int tx = startTileX; tx < endTileX; tx++) {\n            uint8_t tileIndex = tileMap.indices[ty * tileMap.width + tx];\n            if (tileIndex < tileMap.tileCount) {\n                int x = tx * tileMap.tileWidth + offsetX;\n                int y = ty * tileMap.tileHeight + offsetY;\n                renderer.drawSprite(\n                    tileMap.tiles[tileIndex],\n                    x, y,\n                    color,\n                    false\n                );\n            }\n        }\n    }\n}\n

    Note: The built-in drawTileMap() already performs viewport culling, so you typically don't need to implement this yourself.

    "},{"location":"manual/advanced_graphics/tilemaps/#best-practices","title":"Best Practices","text":""},{"location":"manual/advanced_graphics/tilemaps/#tile-design","title":"Tile Design","text":"
    • Keep tiles small: 8x8 or 16x16 pixels work best
    • Reuse tiles: Design tiles that can be used in multiple ways
    • Consistent style: All tiles should match visually
    • Limit tile count: Too many unique tiles uses more memory
    "},{"location":"manual/advanced_graphics/tilemaps/#level-design","title":"Level Design","text":"
    • Use indices efficiently: 0 = empty, 1+ = different tiles
    • Plan layout: Design level on paper/grid first
    • Test on hardware: Large tilemaps may impact performance
    • Optimize data: Use compact level data format
    "},{"location":"manual/advanced_graphics/tilemaps/#performance","title":"Performance","text":"
    • Limit tilemap size: Very large tilemaps can be slow
    • Use appropriate tile size: Smaller tiles = more tiles to draw
    • Combine with culling: Only draw visible area
    • Test scrolling: Ensure smooth scrolling performance
    "},{"location":"manual/advanced_graphics/tilemaps/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/advanced_graphics/tilemaps/#level-data-in-code","title":"Level Data in Code","text":"
    // Define level as 2D array (easier to read)\nstatic const uint8_t LEVEL_DATA[][TILEMAP_WIDTH] = {\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, // Ground\n};\n\n// Copy to tilemap indices\nvoid loadLevel() {\n    for (int y = 0; y < TILEMAP_HEIGHT; y++) {\n        for (int x = 0; x < TILEMAP_WIDTH; x++) {\n            TILEMAP_INDICES[y * TILEMAP_WIDTH + x] = LEVEL_DATA[y][x];\n        }\n    }\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#collision-detection-with-tilemaps","title":"Collision Detection with Tilemaps","text":"
    bool isTileSolid(int tileX, int tileY) {\n    if (tileX < 0 || tileX >= TILEMAP_WIDTH ||\n        tileY < 0 || tileY >= TILEMAP_HEIGHT) {\n        return true; // Out of bounds = solid\n    }\n\n    uint8_t tileIndex = TILEMAP_INDICES[tileY * TILEMAP_WIDTH + tileX];\n    return tileIndex != 0; // 0 = empty, others = solid\n}\n\nbool checkCollision(float x, float y, int width, int height) {\n    // Convert world position to tile coordinates\n    int tileX1 = static_cast<int>(x) / TILE_SIZE;\n    int tileY1 = static_cast<int>(y) / TILE_SIZE;\n    int tileX2 = static_cast<int>(x + width) / TILE_SIZE;\n    int tileY2 = static_cast<int>(y + height) / TILE_SIZE;\n\n    // Check all tiles actor overlaps\n    for (int ty = tileY1; ty <= tileY2; ty++) {\n        for (int tx = tileX1; tx <= tileX2; tx++) {\n            if (isTileSolid(tx, ty)) {\n                return true; // Collision!\n            }\n        }\n    }\n\n    return false; // No collision\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/advanced_graphics/tilemaps/#tiles-not-appearing","title":"Tiles Not Appearing","text":"
    • Verify tile indices are correct (0 = first tile, 1 = second, etc.)
    • Check tilemap dimensions match indices array size
    • Ensure tiles array has enough entries
    • Verify tile size matches sprite size
    "},{"location":"manual/advanced_graphics/tilemaps/#performance-issues","title":"Performance Issues","text":"
    • Reduce tilemap size
    • Use smaller tiles
    • Limit number of unique tiles
    • Test viewport culling
    "},{"location":"manual/advanced_graphics/tilemaps/#scrolling-problems","title":"Scrolling Problems","text":"
    • Ensure camera is applied before drawing tilemap
    • Check tilemap position matches camera offset
    • Verify tilemap boundaries are correct
    • Test with simple tilemap first
    "},{"location":"manual/advanced_graphics/tilemaps/#next-steps","title":"Next Steps","text":"

    Now that you understand tilemaps, learn about: - Particles and Effects - Add visual effects - Cameras and Scrolling - Combine with scrolling - Performance Optimization - Optimize rendering

    See also: - API Reference - TileMap - API Reference - Renderer - Manual - Cameras and Scrolling

    "},{"location":"manual/game_development/audio/","title":"Audio","text":"

    PixelRoot32 includes a complete NES-like audio system with 4 channels for sound effects and background music. This guide shows you how to add sound and music to your games.

    "},{"location":"manual/game_development/audio/#audio-configuration","title":"Audio Configuration","text":"

    Before using audio, you need to configure an AudioBackend. This is done when creating the Engine:

    "},{"location":"manual/game_development/audio/#esp32-internal-dac","title":"ESP32: Internal DAC","text":"
    #include <drivers/esp32/ESP32_DAC_AudioBackend.h>\n\nconst int DAC_PIN = 25; // GPIO 25 or 26\npr32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(DAC_PIN, 11025);\n\npr32::audio::AudioConfig audioConfig(&audioBackend, audioBackend.getSampleRate());\n
    "},{"location":"manual/game_development/audio/#esp32-external-i2s-dac","title":"ESP32: External I2S DAC","text":"
    #include <drivers/esp32/ESP32_I2S_AudioBackend.h>\n\nconst int I2S_BCLK = 26;  // Bit clock\nconst int I2S_LRCK = 25;  // Left/Right clock\nconst int I2S_DOUT = 22;  // Data out\n\npr32::drivers::esp32::ESP32_I2S_AudioBackend audioBackend(\n    I2S_BCLK, I2S_LRCK, I2S_DOUT, 22050\n);\n\npr32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n
    "},{"location":"manual/game_development/audio/#native-pc-sdl2","title":"Native (PC): SDL2","text":"
    #include <drivers/native/SDL2_AudioBackend.h>\n\npr32::drivers::native::SDL2_AudioBackend audioBackend(22050, 1024);\n\npr32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n
    "},{"location":"manual/game_development/audio/#sound-effects","title":"Sound Effects","text":"

    Sound effects are created using AudioEvent structures and played through the AudioEngine.

    "},{"location":"manual/game_development/audio/#audioevent-structure","title":"AudioEvent Structure","text":"
    #include <audio/AudioTypes.h>\n\npr32::audio::AudioEvent soundEffect{};\nsoundEffect.type = pr32::audio::WaveType::PULSE;  // Waveform type\nsoundEffect.frequency = 1500.0f;                  // Frequency in Hz\nsoundEffect.duration = 0.12f;                      // Duration in seconds\nsoundEffect.volume = 0.8f;                         // Volume (0.0 to 1.0)\nsoundEffect.duty = 0.5f;                           // Duty cycle (for PULSE only)\n
    "},{"location":"manual/game_development/audio/#wave-types","title":"Wave Types","text":"

    PixelRoot32 supports three wave types:

    • PULSE: Square wave with variable duty cycle
    • Duty cycles: 0.125 (thin), 0.25 (classic NES), 0.5 (symmetric), 0.75 (fat)
    • Good for: Beeps, jumps, UI sounds, leads

    • TRIANGLE: Triangle wave (fixed volume/duty)

    • Softer, smoother sound
    • Good for: Bass lines, pads, background tones

    • NOISE: Pseudo-random noise

    • Harsh, chaotic sound
    • Good for: Explosions, hits, impacts, drums
    "},{"location":"manual/game_development/audio/#playing-sound-effects","title":"Playing Sound Effects","text":"
    // Get the audio engine\nauto& audio = engine.getAudioEngine();\n\n// Create and play a sound\npr32::audio::AudioEvent jumpSound{};\njumpSound.type = pr32::audio::WaveType::PULSE;\njumpSound.frequency = 800.0f;\njumpSound.duration = 0.1f;\njumpSound.volume = 0.7f;\njumpSound.duty = 0.25f;\n\naudio.playEvent(jumpSound);\n
    "},{"location":"manual/game_development/audio/#common-sound-effects","title":"Common Sound Effects","text":"

    Here are some example sound effects you can use:

    namespace SoundEffects {\n    // Jump sound\n    inline pr32::audio::AudioEvent jump() {\n        pr32::audio::AudioEvent evt{};\n        evt.type = pr32::audio::WaveType::PULSE;\n        evt.frequency = 600.0f;\n        evt.duration = 0.1f;\n        evt.volume = 0.7f;\n        evt.duty = 0.25f;\n        return evt;\n    }\n\n    // Coin/collect sound\n    inline pr32::audio::AudioEvent coin() {\n        pr32::audio::AudioEvent evt{};\n        evt.type = pr32::audio::WaveType::PULSE;\n        evt.frequency = 1500.0f;\n        evt.duration = 0.12f;\n        evt.volume = 0.8f;\n        evt.duty = 0.5f;\n        return evt;\n    }\n\n    // Explosion\n    inline pr32::audio::AudioEvent explosion() {\n        pr32::audio::AudioEvent evt{};\n        evt.type = pr32::audio::WaveType::NOISE;\n        evt.frequency = 200.0f;\n        evt.duration = 0.3f;\n        evt.volume = 0.9f;\n        return evt;\n    }\n\n    // Hit/damage\n    inline pr32::audio::AudioEvent hit() {\n        pr32::audio::AudioEvent evt{};\n        evt.type = pr32::audio::WaveType::NOISE;\n        evt.frequency = 300.0f;\n        evt.duration = 0.15f;\n        evt.volume = 0.6f;\n        return evt;\n    }\n}\n\n// Usage\naudio.playEvent(SoundEffects::jump());\n
    "},{"location":"manual/game_development/audio/#background-music","title":"Background Music","text":"

    Background music uses the MusicPlayer system, which sequences notes over time.

    "},{"location":"manual/game_development/audio/#music-notes","title":"Music Notes","text":"

    Music is built from MusicNote structures:

    #include <audio/AudioMusicTypes.h>\n\nusing namespace pr32::audio;\n\nMusicNote note{};\nnote.note = Note::C;        // Musical note (C, D, E, F, G, A, B, or Rest)\nnote.octave = 4;            // Octave (0-8)\nnote.duration = 0.2f;       // Duration in seconds\nnote.volume = 0.7f;         // Volume (0.0 to 1.0)\n
    "},{"location":"manual/game_development/audio/#instrument-presets","title":"Instrument Presets","text":"

    For convenience, use predefined instrument presets:

    using namespace pr32::audio;\n\n// Available presets:\n// - INSTR_PULSE_LEAD: Main lead pulse (octave 4)\n// - INSTR_PULSE_BASS: Bass pulse (octave 3)\n// - INSTR_PULSE_CHIP_HIGH: High-pitched chiptune (octave 5)\n// - INSTR_TRIANGLE_PAD: Soft triangle pad (octave 4)\n
    "},{"location":"manual/game_development/audio/#creating-a-melody","title":"Creating a Melody","text":"
    #include <audio/AudioMusicTypes.h>\n\nusing namespace pr32::audio;\n\n// Define melody notes\nstatic const MusicNote MELODY_NOTES[] = {\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::E, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::G, 0.25f),\n    makeRest(0.10f),  // Rest (silence)\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::E, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::G, 0.25f),\n    makeRest(0.10f),\n};\n\n// Create music track\nstatic const MusicTrack GAME_MUSIC = {\n    MELODY_NOTES,                              // notes array\n    sizeof(MELODY_NOTES) / sizeof(MusicNote),  // note count\n    true,                                       // loop\n    WaveType::PULSE,                           // channel type\n    0.5f                                       // duty cycle\n};\n
    "},{"location":"manual/game_development/audio/#playing-music","title":"Playing Music","text":"
    // Get the music player\nauto& music = engine.getMusicPlayer();\n\n// Play a track\nmusic.play(GAME_MUSIC);\n\n// Control playback\nmusic.stop();   // Stop playback\nmusic.pause();  // Pause (time doesn't advance)\nmusic.resume(); // Resume after pause\n\n// Check status\nif (music.isPlaying()) {\n    // Music is currently playing\n}\n
    "},{"location":"manual/game_development/audio/#music-in-scene","title":"Music in Scene","text":"

    Typically, you start music in your scene's init():

    void MyGameScene::init() override {\n    // Start background music\n    engine.getMusicPlayer().play(GAME_MUSIC);\n\n    // ... rest of initialization\n}\n
    "},{"location":"manual/game_development/audio/#master-volume","title":"Master Volume","text":"

    Control overall volume without changing individual sounds:

    auto& audio = engine.getAudioEngine();\n\n// Set master volume (0.0 to 1.0)\naudio.setMasterVolume(0.5f); // 50% volume\n\n// Get current volume\nfloat currentVolume = audio.getMasterVolume();\n
    "},{"location":"manual/game_development/audio/#complete-example","title":"Complete Example","text":"

    Here's a complete example combining sound effects and music:

    #include <core/Scene.h>\n#include <audio/AudioTypes.h>\n#include <audio/AudioMusicTypes.h>\n\nusing namespace pr32::audio;\n\n// Background music\nstatic const MusicNote GAME_MELODY[] = {\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::E, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::G, 0.25f),\n    makeRest(0.10f),\n};\n\nstatic const MusicTrack BACKGROUND_MUSIC = {\n    GAME_MELODY,\n    sizeof(GAME_MELODY) / sizeof(MusicNote),\n    true,  // loop\n    WaveType::PULSE,\n    0.5f\n};\n\nclass AudioExampleScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Start background music\n        engine.getMusicPlayer().play(BACKGROUND_MUSIC);\n\n        // Set master volume\n        engine.getAudioEngine().setMasterVolume(0.8f);\n    }\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        auto& audio = engine.getAudioEngine();\n\n        // Play sound effect on button press\n        if (input.isButtonPressed(4)) { // Button A\n            AudioEvent jumpSound{};\n            jumpSound.type = WaveType::PULSE;\n            jumpSound.frequency = 800.0f;\n            jumpSound.duration = 0.1f;\n            jumpSound.volume = 0.7f;\n            jumpSound.duty = 0.25f;\n\n            audio.playEvent(jumpSound);\n        }\n\n        Scene::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/game_development/audio/#designing-nes-like-sounds","title":"Designing NES-like Sounds","text":""},{"location":"manual/game_development/audio/#frequency-guidelines","title":"Frequency Guidelines","text":"
    • Low frequencies (200-400 Hz): Bass, impacts, explosions
    • Mid frequencies (400-1000 Hz): Main sounds, jumps, UI
    • High frequencies (1000-2000 Hz): Beeps, coins, pickups
    • Very high (2000+ Hz): Sharp sounds, alerts
    "},{"location":"manual/game_development/audio/#duration-guidelines","title":"Duration Guidelines","text":"
    • Short (0.05-0.1s): UI clicks, small effects
    • Medium (0.1-0.2s): Jumps, hits, pickups
    • Long (0.2-0.5s): Explosions, power-ups, transitions
    "},{"location":"manual/game_development/audio/#duty-cycle-pulse-only","title":"Duty Cycle (PULSE only)","text":"
    • 0.125: Thin, sharp, piercing
    • 0.25: Classic NES lead sound
    • 0.5: Symmetric, full, fat
    • 0.75: Very fat, bass-like
    "},{"location":"manual/game_development/audio/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/audio/#sound-design","title":"Sound Design","text":"
    • Keep sounds short: Long sounds can overlap and cause issues
    • Use appropriate volumes: 0.6-0.8 is usually good for effects
    • Vary frequencies: Don't use the same frequency for everything
    • Test on hardware: ESP32 audio may sound different than PC
    "},{"location":"manual/game_development/audio/#music","title":"Music","text":"
    • Use one channel for music: Leave other channels for SFX
    • Keep melodies simple: Complex melodies can be hard to follow
    • Loop seamlessly: End your melody where it can loop naturally
    • Consider tempo: Faster games need faster music
    "},{"location":"manual/game_development/audio/#performance","title":"Performance","text":"
    • Limit simultaneous sounds: Only 4 channels total
    • Music uses one channel: Plan your SFX accordingly
    • Don't spam sounds: Too many sounds can cause audio glitches
    • Use master volume: Easier than adjusting individual sounds
    "},{"location":"manual/game_development/audio/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/audio/#sound-effect-helper-function","title":"Sound Effect Helper Function","text":"
    void playJumpSound() {\n    auto& audio = engine.getAudioEngine();\n    AudioEvent evt{};\n    evt.type = WaveType::PULSE;\n    evt.frequency = 600.0f;\n    evt.duration = 0.1f;\n    evt.volume = 0.7f;\n    evt.duty = 0.25f;\n    audio.playEvent(evt);\n}\n
    "},{"location":"manual/game_development/audio/#music-state-management","title":"Music State Management","text":"
    class GameScene : public Scene {\n    bool musicStarted = false;\n\n    void init() override {\n        // Don't start music here if scene can be re-initialized\n    }\n\n    void update(unsigned long deltaTime) override {\n        if (!musicStarted) {\n            engine.getMusicPlayer().play(GAME_MUSIC);\n            musicStarted = true;\n        }\n        Scene::update(deltaTime);\n    }\n};\n
    "},{"location":"manual/game_development/audio/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/game_development/audio/#no-sound","title":"No Sound","text":"
    • Check audio backend is configured correctly
    • Verify sample rate matches backend
    • Check master volume is not 0
    • Ensure audio is initialized (engine.init())
    "},{"location":"manual/game_development/audio/#distorted-sound","title":"Distorted Sound","text":"
    • Lower volume levels
    • Reduce sample rate (ESP32 DAC works better at 11025 Hz)
    • Check for too many simultaneous sounds
    • Verify hardware connections (ESP32)
    "},{"location":"manual/game_development/audio/#music-not-playing","title":"Music Not Playing","text":"
    • Check music.isPlaying() status
    • Ensure track is properly defined
    • Verify MusicPlayer is updated (happens automatically)
    • Check that music channel is not being used by SFX
    "},{"location":"manual/game_development/audio/#next-steps","title":"Next Steps","text":"

    Now that you can add audio, learn about: - NES Audio Reference - Advanced audio techniques - Physics and Collisions - Make objects interact - User Interface - Create menus and HUDs

    See also: - API Reference - AudioEngine - API Reference - MusicPlayer - API Reference - Audio Types - Manual - Audio Overview

    "},{"location":"manual/game_development/basic_rendering/","title":"Basic Rendering","text":"

    Rendering is how you draw everything on screen. This guide covers the fundamental drawing operations in PixelRoot32: primitives, sprites, and text.

    "},{"location":"manual/game_development/basic_rendering/#accessing-the-renderer","title":"Accessing the Renderer","text":"

    You can access the renderer in two ways:

    "},{"location":"manual/game_development/basic_rendering/#from-the-engine","title":"From the Engine","text":"
    auto& renderer = engine.getRenderer();\n
    "},{"location":"manual/game_development/basic_rendering/#from-a-scene","title":"From a Scene","text":"
    void MyScene::draw(pixelroot32::graphics::Renderer& renderer) override {\n    // renderer is passed as parameter\n    renderer.drawFilledRectangle(0, 0, 240, 240, Color::Black);\n}\n
    "},{"location":"manual/game_development/basic_rendering/#drawing-primitives","title":"Drawing Primitives","text":""},{"location":"manual/game_development/basic_rendering/#pixels","title":"Pixels","text":"

    Draw a single pixel:

    renderer.drawPixel(100, 100, pixelroot32::graphics::Color::White);\n
    "},{"location":"manual/game_development/basic_rendering/#lines","title":"Lines","text":"

    Draw a line between two points:

    renderer.drawLine(10, 10, 200, 200, pixelroot32::graphics::Color::Red);\n
    "},{"location":"manual/game_development/basic_rendering/#rectangles","title":"Rectangles","text":"

    Draw a rectangle outline:

    renderer.drawRectangle(50, 50, 100, 80, pixelroot32::graphics::Color::Blue);\n

    Draw a filled rectangle:

    renderer.drawFilledRectangle(50, 50, 100, 80, pixelroot32::graphics::Color::Blue);\n
    "},{"location":"manual/game_development/basic_rendering/#circles","title":"Circles","text":"

    Draw a circle outline:

    renderer.drawCircle(120, 120, 30, pixelroot32::graphics::Color::Green);\n

    Draw a filled circle:

    renderer.drawFilledCircle(120, 120, 30, pixelroot32::graphics::Color::Green);\n
    "},{"location":"manual/game_development/basic_rendering/#simple-sprites-1bpp","title":"Simple Sprites (1bpp)","text":"

    Sprites are the primary way to draw game graphics. The standard format is 1bpp (1 bit per pixel), which is memory-efficient and perfect for retro-style graphics.

    "},{"location":"manual/game_development/basic_rendering/#creating-a-sprite-manually","title":"Creating a Sprite Manually","text":"

    A 1bpp sprite is defined as an array of uint16_t, where each value represents one row:

    #include <graphics/Renderer.h>\n\n// Example: 8x8 sprite (8 rows, each row is a uint16_t)\n// Bit 0 = leftmost pixel, bit 7 = rightmost pixel\nstatic const uint16_t MY_SPRITE_DATA[] = {\n    0b00111100,  // Row 0:   ..####..\n    0b01111110,  // Row 1:  .######.\n    0b11111111,  // Row 2:  ########\n    0b11111111,  // Row 3:  ########\n    0b11111111,  // Row 4:  ########\n    0b01111110,  // Row 5:  .######.\n    0b00111100,  // Row 6:   ..####..\n    0b00000000   // Row 7:  ........\n};\n\n// Create sprite descriptor\nstatic const pixelroot32::graphics::Sprite MY_SPRITE = {\n    MY_SPRITE_DATA,  // data pointer\n    8,                // width\n    8                 // height\n};\n
    "},{"location":"manual/game_development/basic_rendering/#drawing-a-sprite","title":"Drawing a Sprite","text":"
    renderer.drawSprite(\n    MY_SPRITE,                                    // sprite\n    100,                                          // x position\n    100,                                          // y position\n    pixelroot32::graphics::Color::White,         // color\n    false                                         // flipX (optional, default false)\n);\n
    "},{"location":"manual/game_development/basic_rendering/#sprite-bit-convention","title":"Sprite Bit Convention","text":"
    • Each uint16_t represents one row
    • Bit 0 (rightmost bit) = leftmost pixel
    • Bit (width - 1) = rightmost pixel
    • 0 = transparent/off, 1 = on (colored)

    Example: For an 8-pixel wide sprite:

    Row value: 0b00111100\nPixels:    ..####..\n           ^      ^\n         bit 7  bit 0 (leftmost)\n

    "},{"location":"manual/game_development/basic_rendering/#flipping-sprites","title":"Flipping Sprites","text":"

    You can flip a sprite horizontally:

    renderer.drawSprite(MY_SPRITE, 100, 100, Color::White, true); // flipped\n
    "},{"location":"manual/game_development/basic_rendering/#text-rendering","title":"Text Rendering","text":"

    PixelRoot32 uses a native bitmap font system for pixel-perfect text rendering.

    "},{"location":"manual/game_development/basic_rendering/#drawing-text","title":"Drawing Text","text":"
    renderer.drawText(\n    \"Hello World!\",                           // text string\n    10,                                       // x position\n    20,                                       // y position\n    pixelroot32::graphics::Color::White,     // color\n    1                                         // size multiplier (1=normal, 2=double, etc.)\n);\n
    "},{"location":"manual/game_development/basic_rendering/#centered-text","title":"Centered Text","text":"
    renderer.drawTextCentered(\n    \"Game Over\",                              // text\n    120,                                      // y position (centered horizontally)\n    pixelroot32::graphics::Color::Red,       // color\n    2                                         // size\n);\n
    "},{"location":"manual/game_development/basic_rendering/#text-size","title":"Text Size","text":"

    The size parameter multiplies the font size: - 1 = normal size (5x7 pixels per character with default font) - 2 = double size (10x14 pixels) - 3 = triple size (15x21 pixels) - etc.

    "},{"location":"manual/game_development/basic_rendering/#supported-characters","title":"Supported Characters","text":"

    The default font (FONT_5X7) supports ASCII characters 32-126: - Letters: A-Z, a-z - Numbers: 0-9 - Symbols: !@#$%^&*()_+-=[]{}|;:'\",.<>?/ etc.

    "},{"location":"manual/game_development/basic_rendering/#render-layers","title":"Render Layers","text":"

    Entities are drawn in order based on their renderLayer property:

    • Layer 0 (Background): Drawn first (behind everything)
    • Layer 1 (Gameplay): Drawn second (main game objects)
    • Layer 2 (UI): Drawn last (on top of everything)

    Set the render layer when creating an entity:

    myEntity->setRenderLayer(0); // Background\nmyEntity->setRenderLayer(1); // Gameplay (default)\nmyEntity->setRenderLayer(2); // UI\n
    "},{"location":"manual/game_development/basic_rendering/#complete-example","title":"Complete Example","text":"

    Here's a complete example that draws various primitives and a sprite:

    #include <core/Scene.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\n// Simple sprite data (8x8 circle)\nstatic const uint16_t CIRCLE_SPRITE_DATA[] = {\n    0b00111100,\n    0b01111110,\n    0b11111111,\n    0b11111111,\n    0b11111111,\n    0b11111111,\n    0b01111110,\n    0b00111100\n};\n\nstatic const pixelroot32::graphics::Sprite CIRCLE_SPRITE = {\n    CIRCLE_SPRITE_DATA,\n    8,\n    8\n};\n\nclass RenderingExampleScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Set palette\n        pixelroot32::graphics::setPalette(\n            pixelroot32::graphics::PaletteType::NES\n        );\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw background (layer 0)\n        renderer.drawFilledRectangle(0, 0, 240, 240, \n            pixelroot32::graphics::Color::Navy);\n\n        // Draw primitives (layer 1)\n        renderer.drawRectangle(10, 10, 100, 80, \n            pixelroot32::graphics::Color::White);\n        renderer.drawFilledCircle(120, 120, 30, \n            pixelroot32::graphics::Color::Red);\n        renderer.drawLine(0, 0, 240, 240, \n            pixelroot32::graphics::Color::Yellow);\n\n        // Draw sprite\n        renderer.drawSprite(CIRCLE_SPRITE, 50, 50, \n            pixelroot32::graphics::Color::Cyan);\n\n        // Draw text (layer 2 - UI)\n        renderer.drawText(\"Score: 100\", 10, 10, \n            pixelroot32::graphics::Color::White, 1);\n        renderer.drawTextCentered(\"Rendering Demo\", 200, \n            pixelroot32::graphics::Color::Yellow, 2);\n\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/game_development/basic_rendering/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/basic_rendering/#performance","title":"Performance","text":"
    • Minimize draw calls: Batch similar operations when possible
    • Use render layers efficiently: Don't mix layers unnecessarily
    • Avoid drawing off-screen: Check bounds before drawing
    • Reuse sprites: Define sprites once, use many times
    "},{"location":"manual/game_development/basic_rendering/#organization","title":"Organization","text":"
    • Define sprites as static const: Keep them in flash memory
    • Use meaningful names: Name your sprites clearly
    • Group related sprites: Organize sprite data logically
    • Document complex sprites: Comment sprite bit patterns if needed
    "},{"location":"manual/game_development/basic_rendering/#text","title":"Text","text":"
    • Avoid frequent text updates: Text rendering has overhead
    • Use appropriate sizes: Larger text uses more memory
    • Cache text dimensions: Use FontManager::textWidth() if needed
    • Keep text simple: Complex formatting is not supported
    "},{"location":"manual/game_development/basic_rendering/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/basic_rendering/#drawing-a-background","title":"Drawing a Background","text":"
    void drawBackground(Renderer& renderer) {\n    // Solid color background\n    renderer.drawFilledRectangle(0, 0, 240, 240, Color::Black);\n\n    // Or use a tilemap (see Advanced Graphics section)\n}\n
    "},{"location":"manual/game_development/basic_rendering/#drawing-a-hud","title":"Drawing a HUD","text":"
    void drawHUD(Renderer& renderer, int score, int lives) {\n    char buffer[32];\n    snprintf(buffer, sizeof(buffer), \"Score: %d\", score);\n    renderer.drawText(buffer, 10, 10, Color::White, 1);\n\n    snprintf(buffer, sizeof(buffer), \"Lives: %d\", lives);\n    renderer.drawText(buffer, 10, 20, Color::White, 1);\n}\n
    "},{"location":"manual/game_development/basic_rendering/#drawing-multiple-sprites","title":"Drawing Multiple Sprites","text":"
    void drawSpriteArray(Renderer& renderer, const Sprite& sprite, \n                     int count, int startX, int startY, int spacing) {\n    for (int i = 0; i < count; i++) {\n        int x = startX + (i * (sprite.width + spacing));\n        renderer.drawSprite(sprite, x, startY, Color::White);\n    }\n}\n
    "},{"location":"manual/game_development/basic_rendering/#next-steps","title":"Next Steps","text":"

    Now that you can draw basic graphics, learn about: - Sprites and Animation - Advanced sprite techniques - Input and Control - Make your game interactive - Palettes - Use different color schemes

    See also: - API Reference - Renderer - API Reference - Sprite - Render Layers

    "},{"location":"manual/game_development/input_and_control/","title":"Input and Control","text":"

    Handling user input is essential for any interactive game. This guide covers how to read and process input from buttons (ESP32) or keyboard (Native) in PixelRoot32.

    "},{"location":"manual/game_development/input_and_control/#input-configuration","title":"Input Configuration","text":"

    Before you can read input, you need to configure the InputManager. This is done when creating the Engine:

    "},{"location":"manual/game_development/input_and_control/#esp32-configuration","title":"ESP32 Configuration","text":"
    #include <input/InputConfig.h>\n\n// InputConfig(buttonCount, UP, DOWN, LEFT, RIGHT, A, B)\npr32::input::InputConfig inputConfig(\n    6,      // Total number of buttons\n    32,     // UP button GPIO pin\n    27,     // DOWN button GPIO pin\n    33,     // LEFT button GPIO pin\n    14,     // RIGHT button GPIO pin\n    13,     // A button GPIO pin\n    12      // B button GPIO pin\n);\n
    "},{"location":"manual/game_development/input_and_control/#native-pc-configuration","title":"Native (PC) Configuration","text":"
    #include <SDL2/SDL.h>\n#include <input/InputConfig.h>\n\n// InputConfig(buttonCount, UP, DOWN, LEFT, RIGHT, A, B)\npr32::input::InputConfig inputConfig(\n    6,                      // Total number of buttons\n    SDL_SCANCODE_UP,        // UP key\n    SDL_SCANCODE_DOWN,      // DOWN key\n    SDL_SCANCODE_LEFT,      // LEFT key\n    SDL_SCANCODE_RIGHT,     // RIGHT key\n    SDL_SCANCODE_SPACE,     // A button (Space)\n    SDL_SCANCODE_RETURN     // B button (Enter)\n);\n
    "},{"location":"manual/game_development/input_and_control/#reading-input","title":"Reading Input","text":"

    Access the InputManager through the Engine:

    auto& input = engine.getInputManager();\n
    "},{"location":"manual/game_development/input_and_control/#input-states","title":"Input States","text":"

    The InputManager provides four different ways to check button state:

    "},{"location":"manual/game_development/input_and_control/#1-isbuttonpressed","title":"1. isButtonPressed()","text":"

    Returns true only on the frame when the button was just pressed:

    if (input.isButtonPressed(4)) { // Button A (index 4)\n    // This code runs only once when button is first pressed\n    jump();\n}\n

    Use for: Actions that should trigger once per press (jump, shoot, select menu item).

    "},{"location":"manual/game_development/input_and_control/#2-isbuttonreleased","title":"2. isButtonReleased()","text":"

    Returns true only on the frame when the button was just released:

    if (input.isButtonReleased(4)) {\n    // This code runs only once when button is released\n    stopCharging();\n}\n

    Use for: Actions that trigger on release (charge attacks, menu confirmation).

    "},{"location":"manual/game_development/input_and_control/#3-isbuttondown","title":"3. isButtonDown()","text":"

    Returns true while the button is currently held down:

    if (input.isButtonDown(2)) { // LEFT button (index 2)\n    // This code runs every frame while button is held\n    playerX -= speed * (deltaTime * 0.001f);\n}\n

    Use for: Continuous actions (movement, holding, charging).

    "},{"location":"manual/game_development/input_and_control/#4-isbuttonclicked","title":"4. isButtonClicked()","text":"

    Returns true when the button was pressed and then released:

    if (input.isButtonClicked(4)) {\n    // This code runs once per click (press + release cycle)\n    toggleMenu();\n}\n

    Use for: Toggle actions, menu selections, click interactions.

    "},{"location":"manual/game_development/input_and_control/#button-indices","title":"Button Indices","text":"

    The button indices correspond to the order in InputConfig:

    • Index 0: UP
    • Index 1: DOWN
    • Index 2: LEFT
    • Index 3: RIGHT
    • Index 4: A button
    • Index 5: B button

    For convenience, you can define constants:

    namespace Buttons {\n    constexpr uint8_t UP = 0;\n    constexpr uint8_t DOWN = 1;\n    constexpr uint8_t LEFT = 2;\n    constexpr uint8_t RIGHT = 3;\n    constexpr uint8_t A = 4;\n    constexpr uint8_t B = 5;\n}\n\n// Usage\nif (input.isButtonPressed(Buttons::A)) {\n    // ...\n}\n
    "},{"location":"manual/game_development/input_and_control/#character-control-example","title":"Character Control Example","text":"

    Here's a complete example of character movement:

    #include <core/Actor.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\nclass PlayerActor : public pixelroot32::core::Actor {\npublic:\n    float speed = 100.0f; // pixels per second\n\n    PlayerActor(float x, float y)\n        : Actor(x, y, 16, 16) {\n        setRenderLayer(1);\n    }\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        float dt = deltaTime * 0.001f; // Convert to seconds\n\n        // Horizontal movement\n        if (input.isButtonDown(Buttons::LEFT)) {\n            x -= speed * dt;\n        }\n        if (input.isButtonDown(Buttons::RIGHT)) {\n            x += speed * dt;\n        }\n\n        // Vertical movement\n        if (input.isButtonDown(Buttons::UP)) {\n            y -= speed * dt;\n        }\n        if (input.isButtonDown(Buttons::DOWN)) {\n            y += speed * dt;\n        }\n\n        // Keep player on screen\n        if (x < 0) x = 0;\n        if (x > 224) x = 224;\n        if (y < 0) y = 0;\n        if (y > 224) y = 224;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x),\n            static_cast<int>(y),\n            width,\n            height,\n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Handle collisions\n    }\n};\n
    "},{"location":"manual/game_development/input_and_control/#jumping-example","title":"Jumping Example","text":"

    For platformer-style jumping:

    class PlatformerPlayer : public pixelroot32::core::PhysicsActor {\npublic:\n    bool canJump = true;\n    float jumpForce = 200.0f;\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        float dt = deltaTime * 0.001f;\n\n        // Horizontal movement\n        float moveSpeed = 80.0f;\n        if (input.isButtonDown(Buttons::LEFT)) {\n            setVelocity(-moveSpeed, vy);\n        } else if (input.isButtonDown(Buttons::RIGHT)) {\n            setVelocity(moveSpeed, vy);\n        } else {\n            setVelocity(0, vy);\n        }\n\n        // Jump (only on press, and only if on ground)\n        if (input.isButtonPressed(Buttons::A) && canJump) {\n            setVelocity(vx, -jumpForce);\n            canJump = false;\n        }\n\n        // Check if on ground (for jump reset)\n        auto collisionInfo = getWorldCollisionInfo();\n        if (collisionInfo.bottom) {\n            canJump = true;\n        }\n\n        PhysicsActor::update(deltaTime);\n    }\n};\n
    "},{"location":"manual/game_development/input_and_control/#shooting-example","title":"Shooting Example","text":"

    For shooting projectiles:

    class ShooterActor : public pixelroot32::core::Actor {\nprivate:\n    bool fireInputReady = true;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n\n        // Shooting (with cooldown)\n        if (input.isButtonPressed(Buttons::A) && fireInputReady) {\n            shoot();\n            fireInputReady = false;\n        }\n\n        // Reset fire input when button is released\n        if (input.isButtonReleased(Buttons::A)) {\n            fireInputReady = true;\n        }\n    }\n\n    void shoot() {\n        // Create projectile\n        // ...\n    }\n};\n
    "},{"location":"manual/game_development/input_and_control/#menu-navigation-example","title":"Menu Navigation Example","text":"

    For menu navigation:

    class MenuScene : public pixelroot32::core::Scene {\nprivate:\n    int selectedIndex = 0;\n    static const int MENU_ITEMS = 3;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n\n        // Navigate menu\n        if (input.isButtonPressed(Buttons::UP)) {\n            selectedIndex--;\n            if (selectedIndex < 0) selectedIndex = MENU_ITEMS - 1;\n        }\n\n        if (input.isButtonPressed(Buttons::DOWN)) {\n            selectedIndex++;\n            if (selectedIndex >= MENU_ITEMS) selectedIndex = 0;\n        }\n\n        // Select menu item\n        if (input.isButtonPressed(Buttons::A)) {\n            selectMenuItem(selectedIndex);\n        }\n\n        Scene::update(deltaTime);\n    }\n};\n
    "},{"location":"manual/game_development/input_and_control/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/input_and_control/#frame-rate-independence","title":"Frame-Rate Independence","text":"

    Always multiply movement by delta time:

    // \u2705 GOOD: Framerate-independent\nx += speed * (deltaTime * 0.001f);\n\n// \u274c BAD: Framerate-dependent\nx += speed;\n
    "},{"location":"manual/game_development/input_and_control/#input-debouncing","title":"Input Debouncing","text":"

    For actions that should only trigger once:

    // Use isButtonPressed() instead of isButtonDown()\nif (input.isButtonPressed(Buttons::A)) {\n    // Triggers once per press\n}\n
    "},{"location":"manual/game_development/input_and_control/#input-buffering","title":"Input Buffering","text":"

    For responsive controls, you can buffer input:

    class InputBuffer {\n    uint8_t bufferedInput = 0;\n\npublic:\n    void update(const InputManager& input) {\n        if (input.isButtonPressed(Buttons::A)) {\n            bufferedInput = Buttons::A;\n        }\n    }\n\n    bool hasBufferedInput() const { return bufferedInput != 0; }\n    uint8_t consumeInput() {\n        uint8_t result = bufferedInput;\n        bufferedInput = 0;\n        return result;\n    }\n};\n
    "},{"location":"manual/game_development/input_and_control/#multiple-input-methods","title":"Multiple Input Methods","text":"

    Support both button presses and held buttons:

    // Allow both tap and hold for rapid fire\nif (input.isButtonPressed(Buttons::A) || \n    (input.isButtonDown(Buttons::A) && rapidFireTimer <= 0)) {\n    shoot();\n    rapidFireTimer = RAPID_FIRE_DELAY;\n}\n
    "},{"location":"manual/game_development/input_and_control/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/input_and_control/#directional-input","title":"Directional Input","text":"
    float getHorizontalInput() {\n    auto& input = engine.getInputManager();\n    float dir = 0.0f;\n    if (input.isButtonDown(Buttons::LEFT)) dir -= 1.0f;\n    if (input.isButtonDown(Buttons::RIGHT)) dir += 1.0f;\n    return dir;\n}\n\nfloat getVerticalInput() {\n    auto& input = engine.getInputManager();\n    float dir = 0.0f;\n    if (input.isButtonDown(Buttons::UP)) dir -= 1.0f;\n    if (input.isButtonDown(Buttons::DOWN)) dir += 1.0f;\n    return dir;\n}\n
    "},{"location":"manual/game_development/input_and_control/#input-state-machine","title":"Input State Machine","text":"

    For complex input handling:

    enum class InputState {\n    IDLE,\n    PRESSED,\n    HELD,\n    RELEASED\n};\n\nInputState getButtonState(const InputManager& input, uint8_t button) {\n    if (input.isButtonPressed(button)) return InputState::PRESSED;\n    if (input.isButtonDown(button)) return InputState::HELD;\n    if (input.isButtonReleased(button)) return InputState::RELEASED;\n    return InputState::IDLE;\n}\n
    "},{"location":"manual/game_development/input_and_control/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/game_development/input_and_control/#button-not-responding","title":"Button Not Responding","text":"
    • Check button indices match your InputConfig
    • Verify GPIO pins (ESP32) or scancodes (Native) are correct
    • Ensure InputManager is being updated (happens automatically in Engine)
    "},{"location":"manual/game_development/input_and_control/#input-feels-laggy","title":"Input Feels Laggy","text":"
    • Ensure you're using deltaTime for movement
    • Check that input is read in update(), not draw()
    • Verify framerate is stable
    "},{"location":"manual/game_development/input_and_control/#multiple-triggers","title":"Multiple Triggers","text":"
    • Use isButtonPressed() instead of isButtonDown() for one-time actions
    • Implement input buffering or cooldown timers
    "},{"location":"manual/game_development/input_and_control/#next-steps","title":"Next Steps","text":"

    Now that you can handle input, learn about: - Audio - Add sound effects and music - Physics and Collisions - Make objects interact - User Interface - Create menus and HUDs

    See also: - API Reference - InputManager - API Reference - InputConfig - Manual - Input Overview

    "},{"location":"manual/game_development/physics_and_collisions/","title":"Physics and Collisions","text":"

    PixelRoot32 provides a physics system for moving objects and collision detection. This guide covers PhysicsActor for automatic physics and the collision system for detecting interactions between objects.

    "},{"location":"manual/game_development/physics_and_collisions/#physicsactor","title":"PhysicsActor","text":"

    A PhysicsActor is an Actor that automatically handles physics: velocity, gravity, friction, and world boundary collisions.

    "},{"location":"manual/game_development/physics_and_collisions/#creating-a-physicsactor","title":"Creating a PhysicsActor","text":"
    #include <core/PhysicsActor.h>\n\nclass Ball : public pixelroot32::core::PhysicsActor {\npublic:\n    Ball(float x, float y, float radius)\n        : PhysicsActor(x, y, radius * 2, radius * 2) {\n        setRenderLayer(1);\n\n        // Set physics properties\n        setRestitution(0.8f);  // Bounciness (0.8 = 80% bounce)\n        setFriction(0.1f);     // Friction (0.1 = slight friction)\n\n        // Set world boundaries\n        setWorldSize(240, 240);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Apply gravity\n        // (PhysicsActor handles this automatically, but you can add custom forces)\n\n        // Call parent update to apply physics\n        PhysicsActor::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        int radius = width / 2;\n        renderer.drawFilledCircle(\n            static_cast<int>(x + radius),\n            static_cast<int>(y + radius),\n            radius,\n            pixelroot32::graphics::Color::White\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Handle collision with other actors\n    }\n\n    void onWorldCollision() override {\n        // Called when hitting world boundaries\n        // You can play a sound effect here, for example\n    }\n};\n
    "},{"location":"manual/game_development/physics_and_collisions/#physics-properties","title":"Physics Properties","text":""},{"location":"manual/game_development/physics_and_collisions/#velocity","title":"Velocity","text":"

    Set the velocity directly:

    ball->setVelocity(100.0f, -50.0f); // Move right at 100 px/s, up at 50 px/s\n

    Or modify existing velocity:

    float vx = ball->vx; // Access velocity components (protected, use getter if needed)\nball->setVelocity(vx + 10.0f, ball->vy); // Accelerate horizontally\n
    "},{"location":"manual/game_development/physics_and_collisions/#restitution-bounciness","title":"Restitution (Bounciness)","text":"

    Controls how much energy is conserved in collisions:

    ball->setRestitution(1.0f);  // Perfect bounce (no energy loss)\nball->setRestitution(0.5f);  // 50% energy loss\nball->setRestitution(0.0f);  // No bounce (stops on impact)\n
    "},{"location":"manual/game_development/physics_and_collisions/#friction","title":"Friction","text":"

    Applies gradual velocity reduction:

    ball->setFriction(0.0f);  // No friction (slides forever)\nball->setFriction(0.5f);  // Moderate friction\nball->setFriction(1.0f);  // High friction (stops quickly)\n
    "},{"location":"manual/game_development/physics_and_collisions/#world-boundaries","title":"World Boundaries","text":"

    Set the playable area:

    // Set world size (used as default boundaries)\nball->setWorldSize(240, 240);\n\n// Or set custom boundaries\npixelroot32::core::LimitRect limits(10, 10, 230, 230); // Left, Top, Right, Bottom\nball->setLimits(limits);\n
    "},{"location":"manual/game_development/physics_and_collisions/#world-collision-detection","title":"World Collision Detection","text":"

    Check if the actor hit world boundaries:

    void update(unsigned long deltaTime) override {\n    PhysicsActor::update(deltaTime);\n\n    auto collisionInfo = getWorldCollisionInfo();\n\n    if (collisionInfo.left || collisionInfo.right) {\n        // Hit side walls\n    }\n\n    if (collisionInfo.top || collisionInfo.bottom) {\n        // Hit top/bottom walls\n    }\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#collision-system","title":"Collision System","text":"

    The collision system detects when Actors overlap and triggers callbacks.

    "},{"location":"manual/game_development/physics_and_collisions/#collision-layers","title":"Collision Layers","text":"

    Use bit flags to organize actors into groups:

    // Define layers (typically in GameLayers.h)\nnamespace Layers {\n    constexpr uint16_t PLAYER = 0x0001;    // Bit 0\n    constexpr uint16_t ENEMY = 0x0002;     // Bit 1\n    constexpr uint16_t PROJECTILE = 0x0004; // Bit 2\n    constexpr uint16_t WALL = 0x0008;      // Bit 3\n    constexpr uint16_t PICKUP = 0x0010;    // Bit 4\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#setting-up-collisions","title":"Setting Up Collisions","text":"

    Configure each actor's collision layer and mask:

    class PlayerActor : public pixelroot32::core::Actor {\npublic:\n    PlayerActor(float x, float y)\n        : Actor(x, y, 16, 16) {\n        // This actor belongs to the PLAYER layer\n        setCollisionLayer(Layers::PLAYER);\n\n        // This actor can collide with ENEMY, WALL, and PICKUP\n        setCollisionMask(Layers::ENEMY | Layers::WALL | Layers::PICKUP);\n    }\n\n    // ... rest of implementation\n};\n\nclass EnemyActor : public pixelroot32::core::Actor {\npublic:\n    EnemyActor(float x, float y)\n        : Actor(x, y, 16, 16) {\n        // This actor belongs to the ENEMY layer\n        setCollisionLayer(Layers::ENEMY);\n\n        // This actor can collide with PLAYER and PROJECTILE\n        setCollisionMask(Layers::PLAYER | Layers::PROJECTILE);\n    }\n\n    // ... rest of implementation\n};\n
    "},{"location":"manual/game_development/physics_and_collisions/#collision-detection","title":"Collision Detection","text":"

    Collisions are automatically detected by the Scene's CollisionSystem. You handle collisions in onCollision():

    void PlayerActor::onCollision(pixelroot32::core::Actor* other) override {\n    // Check what we collided with\n    if (other->isInLayer(Layers::ENEMY)) {\n        // Hit an enemy - take damage\n        takeDamage();\n    } else if (other->isInLayer(Layers::PICKUP)) {\n        // Hit a pickup - collect it\n        collectPickup(other);\n    } else if (other->isInLayer(Layers::WALL)) {\n        // Hit a wall - stop movement\n        stopMovement();\n    }\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#hitbox","title":"Hitbox","text":"

    Define the collision shape:

    pixelroot32::core::Rect getHitBox() override {\n    // Simple AABB (Axis-Aligned Bounding Box)\n    return {x, y, width, height};\n\n    // Or use a smaller hitbox for more forgiving collisions\n    // return {x + 2, y + 2, width - 4, height - 4};\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#complete-example-bouncing-ball","title":"Complete Example: Bouncing Ball","text":"
    #include <core/PhysicsActor.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\nclass BouncingBall : public pixelroot32::core::PhysicsActor {\npublic:\n    BouncingBall(float x, float y, float radius)\n        : PhysicsActor(x, y, radius * 2, radius * 2) {\n        setRenderLayer(1);\n\n        // Physics setup\n        setRestitution(0.9f);  // Very bouncy\n        setFriction(0.05f);    // Low friction\n        setWorldSize(240, 240); // World boundaries\n\n        // Initial velocity\n        setVelocity(50.0f, -30.0f);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Apply gravity\n        float gravity = 200.0f; // pixels per second squared\n        float dt = deltaTime * 0.001f;\n        setVelocity(vx, vy + gravity * dt);\n\n        // Update physics\n        PhysicsActor::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        int radius = width / 2;\n        renderer.drawFilledCircle(\n            static_cast<int>(x + radius),\n            static_cast<int>(y + radius),\n            radius,\n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Bounce off other objects\n        // (PhysicsActor handles world boundaries automatically)\n    }\n\n    void onWorldCollision() override {\n        // Play bounce sound when hitting walls\n        // (Implementation depends on your audio setup)\n    }\n};\n
    "},{"location":"manual/game_development/physics_and_collisions/#complete-example-platformer-player","title":"Complete Example: Platformer Player","text":"
    class PlatformerPlayer : public pixelroot32::core::PhysicsActor {\nprivate:\n    bool onGround = false;\n    float jumpForce = 250.0f;\n    float moveSpeed = 100.0f;\n\npublic:\n    PlatformerPlayer(float x, float y)\n        : PhysicsActor(x, y, 16, 16) {\n        setRenderLayer(1);\n        setFriction(0.3f);  // Ground friction\n        setWorldSize(240, 240);\n\n        // Collision setup\n        setCollisionLayer(Layers::PLAYER);\n        setCollisionMask(Layers::ENEMY | Layers::PLATFORM);\n    }\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        float dt = deltaTime * 0.001f;\n\n        // Horizontal movement\n        float moveDir = 0.0f;\n        if (input.isButtonDown(Buttons::LEFT)) moveDir -= 1.0f;\n        if (input.isButtonDown(Buttons::RIGHT)) moveDir += 1.0f;\n\n        setVelocity(moveDir * moveSpeed, vy);\n\n        // Apply gravity\n        float gravity = 300.0f;\n        setVelocity(vx, vy + gravity * dt);\n\n        // Jump\n        if (input.isButtonPressed(Buttons::A) && onGround) {\n            setVelocity(vx, -jumpForce);\n            onGround = false;\n        }\n\n        // Update physics\n        PhysicsActor::update(deltaTime);\n\n        // Check if on ground\n        auto collisionInfo = getWorldCollisionInfo();\n        onGround = collisionInfo.bottom;\n\n        // Also check collision with platforms\n        // (This would be handled in onCollision)\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        if (other->isInLayer(Layers::PLATFORM)) {\n            // Land on platform\n            auto platformRect = other->getHitBox();\n            if (y + height <= platformRect.y + 5) { // Within 5 pixels of top\n                y = platformRect.y - height;\n                vy = 0;\n                onGround = true;\n            }\n        } else if (other->isInLayer(Layers::ENEMY)) {\n            // Hit enemy\n            takeDamage();\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x),\n            static_cast<int>(y),\n            width,\n            height,\n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n};\n
    "},{"location":"manual/game_development/physics_and_collisions/#sweep-tests","title":"Sweep Tests","text":"

    For fast-moving projectiles, use sweep tests to detect collisions between positions:

    #include <physics/CollisionPrimitives.h>\n\nbool checkProjectileHit(PhysicsActor* projectile, Actor* target) {\n    // Get previous and current positions\n    float prevX = projectile->x - (projectile->vx * deltaTime * 0.001f);\n    float prevY = projectile->y - (projectile->vy * deltaTime * 0.001f);\n\n    // Create circles for sweep test\n    pixelroot32::physics::Circle startCircle;\n    startCircle.x = prevX + projectile->width / 2;\n    startCircle.y = prevY + projectile->height / 2;\n    startCircle.radius = projectile->width / 2;\n\n    pixelroot32::physics::Circle endCircle;\n    endCircle.x = projectile->x + projectile->width / 2;\n    endCircle.y = projectile->y + projectile->height / 2;\n    endCircle.radius = projectile->width / 2;\n\n    // Get target rectangle\n    auto targetRect = target->getHitBox();\n\n    // Perform sweep test\n    float tHit;\n    if (pixelroot32::physics::sweepCircleVsRect(\n        startCircle, endCircle, targetRect, tHit)) {\n        // Collision detected at time tHit (0.0 = at start, 1.0 = at end)\n        return true;\n    }\n\n    return false;\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/physics_and_collisions/#collision-layers_1","title":"Collision Layers","text":"
    • Plan your layers: Design the layer system before coding
    • Use bit flags: Makes combining layers easy with | operator
    • Keep it simple: Don't create too many layers
    • Document layers: Create a GameLayers.h file with all definitions
    "},{"location":"manual/game_development/physics_and_collisions/#physics","title":"Physics","text":"
    • Use appropriate values: Test gravity, speed, and forces
    • Frame-rate independence: Always use deltaTime
    • Limit world size: Keep boundaries reasonable
    • Test on hardware: Physics may behave differently on ESP32 vs PC
    "},{"location":"manual/game_development/physics_and_collisions/#performance","title":"Performance","text":"
    • Limit active actors: Fewer actors = faster collision checks
    • Use layers efficiently: Reduce unnecessary collision pairs
    • Simple hitboxes: AABB is fast, complex shapes are slow
    • Sweep tests sparingly: Only for fast-moving objects
    "},{"location":"manual/game_development/physics_and_collisions/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/physics_and_collisions/#collision-layer-helper","title":"Collision Layer Helper","text":"
    namespace CollisionLayers {\n    constexpr uint16_t PLAYER = 0x0001;\n    constexpr uint16_t ENEMY = 0x0002;\n    constexpr uint16_t PROJECTILE = 0x0004;\n    constexpr uint16_t WALL = 0x0008;\n    constexpr uint16_t PICKUP = 0x0010;\n\n    // Helper to check if actor is in specific layer\n    bool isPlayer(Actor* actor) {\n        return actor->isInLayer(PLAYER);\n    }\n\n    bool isEnemy(Actor* actor) {\n        return actor->isInLayer(ENEMY);\n    }\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#platform-collision","title":"Platform Collision","text":"
    void PlatformerPlayer::onCollision(Actor* other) override {\n    if (other->isInLayer(Layers::PLATFORM)) {\n        auto platform = other->getHitBox();\n\n        // Check if landing on top of platform\n        float playerBottom = y + height;\n        float platformTop = platform.y;\n\n        if (playerBottom <= platformTop + 5 && vy > 0) {\n            // Land on platform\n            y = platformTop - height;\n            vy = 0;\n            onGround = true;\n        }\n    }\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/game_development/physics_and_collisions/#collisions-not-detected","title":"Collisions Not Detected","text":"
    • Verify collision layers and masks overlap
    • Check that actors are added to the scene
    • Ensure Scene::update() is called
    • Verify hitboxes are correct
    "},{"location":"manual/game_development/physics_and_collisions/#physics-too-fastslow","title":"Physics Too Fast/Slow","text":"
    • Adjust values based on deltaTime
    • Check gravity and velocity values
    • Test on actual hardware (ESP32 may run slower)
    "},{"location":"manual/game_development/physics_and_collisions/#objects-passing-through","title":"Objects Passing Through","text":"
    • Use sweep tests for fast objects
    • Increase collision detection frequency
    • Check hitbox sizes match visual size
    "},{"location":"manual/game_development/physics_and_collisions/#next-steps","title":"Next Steps","text":"

    Now that you understand physics and collisions, learn about: - User Interface - Create menus and HUDs - Advanced Graphics - Advanced sprite techniques - Camera and Scrolling - Create scrolling levels

    See also: - API Reference - PhysicsActor - API Reference - CollisionSystem - Manual - Physics Overview - Manual - Collision Detection

    "},{"location":"manual/game_development/scenes_and_entities/","title":"Scenes and Entities","text":"

    Scenes and entities are the foundation of every PixelRoot32 game. This guide teaches you how to organize your game using scenes and create interactive game objects with entities.

    "},{"location":"manual/game_development/scenes_and_entities/#creating-a-scene","title":"Creating a Scene","text":"

    A Scene represents a screen or level in your game. To create a scene, inherit from pixelroot32::core::Scene and implement the three main methods:

    #include <core/Scene.h>\n#include <graphics/Renderer.h>\n\nclass MyGameScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Called once when the scene is initialized\n        // Set up your scene here: create entities, load resources, etc.\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Called every frame\n        // Update game logic here\n\n        // IMPORTANT: Always call parent update to update all entities\n        Scene::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Called every frame to draw\n        // Draw your scene here\n\n        // IMPORTANT: Always call parent draw to draw all entities\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/game_development/scenes_and_entities/#scene-lifecycle","title":"Scene Lifecycle","text":"
    1. init(): Called once when the scene is set as active
    2. Create and initialize entities
    3. Set up game state
    4. Load resources
    5. Configure palettes, audio, etc.

    6. update(deltaTime): Called every frame

    7. Process input
    8. Update game logic
    9. Handle collisions
    10. Must call Scene::update(deltaTime) to update all entities

    11. draw(renderer): Called every frame

    12. Draw background elements
    13. Draw UI elements
    14. Must call Scene::draw(renderer) to draw all entities
    "},{"location":"manual/game_development/scenes_and_entities/#basic-entities","title":"Basic Entities","text":"

    An Entity is any object in your game. To create an entity, inherit from pixelroot32::core::Entity:

    #include <core/Entity.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\nclass SimpleEntity : public pixelroot32::core::Entity {\npublic:\n    SimpleEntity(float x, float y)\n        : Entity(x, y, 16, 16, pixelroot32::core::EntityType::GENERIC) {\n        // Set render layer (0=background, 1=gameplay, 2=UI)\n        setRenderLayer(1);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Update entity logic\n        // For example, move the entity\n        this->x += 1.0f * (deltaTime * 0.001f); // Move right at 1 pixel per second\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw the entity\n        renderer.drawFilledRectangle(\n            static_cast<int>(x), \n            static_cast<int>(y), \n            width, \n            height, \n            pixelroot32::graphics::Color::Red\n        );\n    }\n};\n
    "},{"location":"manual/game_development/scenes_and_entities/#entity-properties","title":"Entity Properties","text":"

    Every entity has these properties:

    • x, y: Position in world space (float)
    • width, height: Dimensions (int)
    • isVisible: If false, draw() is not called
    • isEnabled: If false, update() is not called
    • renderLayer: Which layer to draw on (0, 1, or 2)
    "},{"location":"manual/game_development/scenes_and_entities/#adding-entities-to-a-scene","title":"Adding Entities to a Scene","text":"

    Add entities to your scene in init():

    void MyGameScene::init() override {\n    // Create entities\n    SimpleEntity* entity1 = new SimpleEntity(50, 50);\n    SimpleEntity* entity2 = new SimpleEntity(100, 100);\n\n    // Add them to the scene\n    addEntity(entity1);\n    addEntity(entity2);\n}\n

    The scene automatically manages these entities: - Calls update() on all enabled entities each frame - Calls draw() on all visible entities each frame - Handles cleanup when the scene is destroyed

    "},{"location":"manual/game_development/scenes_and_entities/#actors-entities-with-collisions","title":"Actors: Entities with Collisions","text":"

    An Actor is an entity that can participate in collision detection. Inherit from pixelroot32::core::Actor:

    #include <core/Actor.h>\n#include <physics/CollisionTypes.h>\n\nclass MyActor : public pixelroot32::core::Actor {\npublic:\n    MyActor(float x, float y, int w, int h)\n        : Actor(x, y, w, h) {\n        // Set collision layer (what group this actor belongs to)\n        setCollisionLayer(0x0001); // Example: layer 1\n\n        // Set collision mask (what groups this actor can collide with)\n        setCollisionMask(0x0002); // Example: can collide with layer 2\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Update actor logic\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw the actor\n    }\n\n    // REQUIRED: Define the hitbox for collision detection\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    // REQUIRED: Handle collisions\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // React to collision\n        // For example: take damage, destroy self, etc.\n    }\n};\n
    "},{"location":"manual/game_development/scenes_and_entities/#collision-layers-and-masks","title":"Collision Layers and Masks","text":"

    Collision layers use bit flags to organize actors into groups:

    // Define your layers (typically in a GameLayers.h file)\nnamespace Layers {\n    constexpr uint16_t PLAYER = 0x0001;  // Bit 0\n    constexpr uint16_t ENEMY = 0x0002;  // Bit 1\n    constexpr uint16_t PROJECTILE = 0x0004; // Bit 2\n    constexpr uint16_t WALL = 0x0008;   // Bit 3\n}\n\n// Set up a player actor\nplayer->setCollisionLayer(Layers::PLAYER);\nplayer->setCollisionMask(Layers::ENEMY | Layers::WALL); // Can collide with enemies and walls\n\n// Set up an enemy actor\nenemy->setCollisionLayer(Layers::ENEMY);\nenemy->setCollisionMask(Layers::PLAYER | Layers::PROJECTILE); // Can collide with player and projectiles\n

    The collision system only checks collisions between actors whose layers and masks overlap. This is much more efficient than checking every pair.

    "},{"location":"manual/game_development/scenes_and_entities/#scene-management","title":"Scene Management","text":""},{"location":"manual/game_development/scenes_and_entities/#setting-the-active-scene","title":"Setting the Active Scene","text":"

    From your main code, set the active scene:

    MyGameScene gameScene;\n\nvoid setup() {\n    engine.init();\n    gameScene.init();\n    engine.setScene(&gameScene); // Set as active scene\n}\n
    "},{"location":"manual/game_development/scenes_and_entities/#switching-scenes","title":"Switching Scenes","text":"

    To switch to a different scene:

    MenuScene menuScene;\nGameScene gameScene;\n\nvoid switchToGame() {\n    gameScene.init();\n    engine.setScene(&gameScene); // Replaces current scene\n}\n
    "},{"location":"manual/game_development/scenes_and_entities/#scene-stack-pushpop","title":"Scene Stack (Push/Pop)","text":"

    For menus and pause screens, use the scene stack:

    // Push a pause menu (game scene stays in background)\nvoid pauseGame() {\n    pauseMenu.init();\n    engine.getCurrentScene()->getSceneManager().pushScene(&pauseMenu);\n}\n\n// Pop the pause menu (resume game)\nvoid resumeGame() {\n    engine.getCurrentScene()->getSceneManager().popScene();\n}\n

    Note: Scene stack management is handled internally by the Engine's SceneManager. You typically access it through engine.getCurrentScene().

    "},{"location":"manual/game_development/scenes_and_entities/#complete-example","title":"Complete Example","text":"

    Here's a complete example of a scene with multiple entities:

    #include <core/Scene.h>\n#include <core/Entity.h>\n#include <core/Actor.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\n// A simple moving entity\nclass MovingBox : public pixelroot32::core::Entity {\npublic:\n    MovingBox(float x, float y) \n        : Entity(x, y, 20, 20, pixelroot32::core::EntityType::GENERIC),\n          speedX(50.0f), speedY(30.0f) {\n        setRenderLayer(1);\n    }\n\n    void update(unsigned long deltaTime) override {\n        float dt = deltaTime * 0.001f; // Convert to seconds\n\n        x += speedX * dt;\n        y += speedY * dt;\n\n        // Bounce off screen edges\n        if (x < 0 || x > 220) speedX = -speedX;\n        if (y < 0 || y > 220) speedY = -speedY;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x), \n            static_cast<int>(y), \n            width, \n            height, \n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n\nprivate:\n    float speedX, speedY;\n};\n\n// A simple actor that can collide\nclass CollidableBox : public pixelroot32::core::Actor {\npublic:\n    CollidableBox(float x, float y)\n        : Actor(x, y, 30, 30) {\n        setRenderLayer(1);\n        setCollisionLayer(0x0001);\n        setCollisionMask(0x0001);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Static actor, no movement\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x), \n            static_cast<int>(y), \n            width, \n            height, \n            pixelroot32::graphics::Color::Yellow\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Change color when collided\n        // (In a real game, you'd handle collision logic here)\n    }\n};\n\n// The scene\nclass ExampleScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Create and add entities\n        addEntity(new MovingBox(50, 50));\n        addEntity(new MovingBox(150, 100));\n        addEntity(new CollidableBox(100, 100));\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime); // Update all entities\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw background\n        renderer.drawFilledRectangle(0, 0, 240, 240, \n            pixelroot32::graphics::Color::Black);\n\n        Scene::draw(renderer); // Draw all entities\n    }\n};\n
    "},{"location":"manual/game_development/scenes_and_entities/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/scenes_and_entities/#entity-management","title":"Entity Management","text":"
    • Pre-allocate entities: Create entities in init(), not in update()
    • Reuse entities: Instead of creating/destroying, enable/disable entities
    • Limit entity count: MAX_ENTITIES = 32 per scene
    • Use object pooling: For frequently created/destroyed entities (projectiles, particles)
    "},{"location":"manual/game_development/scenes_and_entities/#scene-organization","title":"Scene Organization","text":"
    • One scene per screen: Menu, game, game over, etc.
    • Keep scenes focused: Each scene should have a single responsibility
    • Initialize in init(): Don't do heavy work in the constructor
    • Clean up properly: Remove entities when switching scenes
    "},{"location":"manual/game_development/scenes_and_entities/#collision-layers","title":"Collision Layers","text":"
    • Plan your layers: Design your layer system before coding
    • Use bit flags: Makes layer combinations easy
    • Keep it simple: Don't over-complicate with too many layers
    • Document your layers: Create a GameLayers.h file
    "},{"location":"manual/game_development/scenes_and_entities/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/scenes_and_entities/#entity-pool-pattern","title":"Entity Pool Pattern","text":"

    For entities that are frequently created and destroyed (like projectiles):

    class ProjectilePool {\n    static const int POOL_SIZE = 10;\n    ProjectileActor pool[POOL_SIZE];\n\npublic:\n    ProjectileActor* getAvailable() {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (!pool[i].isActive) {\n                pool[i].isActive = true;\n                return &pool[i];\n            }\n        }\n        return nullptr; // Pool exhausted\n    }\n};\n
    "},{"location":"manual/game_development/scenes_and_entities/#entity-factory-pattern","title":"Entity Factory Pattern","text":"

    Create entities through factory functions:

    Entity* createEnemy(EnemyType type, float x, float y) {\n    switch (type) {\n        case EnemyType::BASIC:\n            return new BasicEnemy(x, y);\n        case EnemyType::FAST:\n            return new FastEnemy(x, y);\n        // ...\n    }\n}\n
    "},{"location":"manual/game_development/scenes_and_entities/#next-steps","title":"Next Steps","text":"

    Now that you understand scenes and entities, learn about: - Basic Rendering - Draw sprites, text, and primitives - Input and Control - Handle user input - Physics and Collisions - Advanced collision handling

    See also: - Fundamental Concepts - API Reference - Scene - API Reference - Entity - API Reference - Actor

    "},{"location":"manual/game_development/user_interface/","title":"User Interface","text":"

    PixelRoot32 provides a complete UI system for creating menus, HUDs, and interface elements. This guide covers all UI components and layout systems.

    "},{"location":"manual/game_development/user_interface/#ui-elements","title":"UI Elements","text":"

    All UI elements inherit from UIElement, which itself inherits from Entity. This means UI elements can be added to scenes just like any other entity.

    "},{"location":"manual/game_development/user_interface/#uilabel","title":"UILabel","text":"

    Display text on screen:

    #include <graphics/ui/UILabel.h>\n\n// Create a label\npixelroot32::graphics::ui::UILabel* scoreLabel = new pixelroot32::graphics::ui::UILabel(\n    \"Score: 0\",                    // text\n    10,                            // x position\n    10,                            // y position\n    pixelroot32::graphics::Color::White,  // color\n    1                              // size multiplier\n);\n\n// Add to scene\naddEntity(scoreLabel);\n\n// Update text dynamically\nscoreLabel->setText(\"Score: 100\");\n\n// Center horizontally\nscoreLabel->centerX(240); // Screen width\n
    "},{"location":"manual/game_development/user_interface/#uibutton","title":"UIButton","text":"

    Create clickable buttons:

    #include <graphics/ui/UIButton.h>\n\n// Create a button\npixelroot32::graphics::ui::UIButton* startButton = new pixelroot32::graphics::ui::UIButton(\n    \"Start Game\",                  // text\n    4,                             // navigation index\n    50,                            // x position\n    100,                           // y position\n    140,                           // width\n    30,                            // height\n    []() {                         // callback function\n        // Button clicked - start game\n        startGame();\n    }\n);\n\n// Configure style\nstartButton->setStyle(\n    pixelroot32::graphics::Color::White,   // text color\n    pixelroot32::graphics::Color::Blue,    // background color\n    true                                    // draw background\n);\n\n// Add to scene\naddEntity(startButton);\n
    "},{"location":"manual/game_development/user_interface/#uicheckbox","title":"UICheckBox","text":"

    Create interactive checkboxes:

    #include <graphics/ui/UICheckBox.h>\n\n// Create a checkbox\npixelroot32::graphics::ui::UICheckBox* soundCheckbox = new pixelroot32::graphics::ui::UICheckBox(\n    \"Enable Sound\",                // text\n    4,                             // navigation index\n    50,                            // x position\n    140,                           // y position\n    140,                           // width\n    20,                            // height\n    true,                          // initial checked state\n    [](bool checked) {             // callback function\n        // Checkbox state changed\n        setSoundEnabled(checked);\n    },\n    1                              // font size\n);\n\n// Configure style\nsoundCheckbox->setStyle(\n    pixelroot32::graphics::Color::White,   // text color\n    pixelroot32::graphics::Color::Blue,    // background color\n    false                                  // draw background\n);\n\n// Add to scene\naddEntity(soundCheckbox);\n
    "},{"location":"manual/game_development/user_interface/#uipanel","title":"UIPanel","text":"

    Create visual containers with background and border:

    #include <graphics/ui/UIPanel.h>\n\n// Create a panel\npixelroot32::graphics::ui::UIPanel* dialog = new pixelroot32::graphics::ui::UIPanel(\n    50,   // x\n    50,   // y\n    140,  // width\n    140   // height\n);\n\n// Configure appearance\ndialog->setBackgroundColor(pixelroot32::graphics::Color::Black);\ndialog->setBorderColor(pixelroot32::graphics::Color::White);\ndialog->setBorderWidth(2);\n\n// Add content (typically a layout)\ndialog->setChild(menuLayout);\n\n// Add to scene\naddEntity(dialog);\n
    "},{"location":"manual/game_development/user_interface/#layouts","title":"Layouts","text":"

    Layouts automatically organize UI elements, eliminating the need for manual position calculations.

    "},{"location":"manual/game_development/user_interface/#uiverticallayout","title":"UIVerticalLayout","text":"

    Organize elements vertically with automatic scrolling:

    #include <graphics/ui/UIVerticalLayout.h>\n\n// Create vertical layout\npixelroot32::graphics::ui::UIVerticalLayout* menu = new pixelroot32::graphics::ui::UIVerticalLayout(\n    10,   // x\n    60,   // y\n    220,  // width\n    160   // height (viewport)\n);\n\n// Configure layout\nmenu->setPadding(5);        // Internal padding\nmenu->setSpacing(6);        // Space between elements\nmenu->setScrollEnabled(true); // Enable scrolling\n\n// Set navigation buttons\nmenu->setNavigationButtons(0, 1); // UP=0, DOWN=1\n\n// Set button styles\nmenu->setButtonStyle(\n    pixelroot32::graphics::Color::White,  // selected text\n    pixelroot32::graphics::Color::Cyan,    // selected background\n    pixelroot32::graphics::Color::White,  // unselected text\n    pixelroot32::graphics::Color::Black   // unselected background\n);\n\n// Add buttons (no manual positioning needed!)\nfor (int i = 0; i < 10; i++) {\n    UIButton* btn = new UIButton(\n        \"Option \" + std::to_string(i),\n        i,\n        0, 0,  // Position ignored - layout handles it\n        200, 20,\n        [i]() { handleOption(i); }\n    );\n    menu->addElement(btn);\n}\n\n// Add layout to scene\nmenu->setRenderLayer(2); // UI layer\naddEntity(menu);\n
    "},{"location":"manual/game_development/user_interface/#uihorizontallayout","title":"UIHorizontalLayout","text":"

    Organize elements horizontally:

    #include <graphics/ui/UIHorizontalLayout.h>\n\n// Create horizontal layout (menu bar)\npixelroot32::graphics::ui::UIHorizontalLayout* menuBar = new pixelroot32::graphics::ui::UIHorizontalLayout(\n    0,    // x\n    0,    // y\n    240,  // width\n    30    // height\n);\n\nmenuBar->setPadding(5);\nmenuBar->setSpacing(4);\nmenuBar->setScrollEnabled(true);\nmenuBar->setNavigationButtons(2, 3); // LEFT=2, RIGHT=3\n\n// Add menu items\nmenuBar->addElement(new UIButton(\"File\", 0, 0, 0, 60, 20, []() {}));\nmenuBar->addElement(new UIButton(\"Edit\", 1, 0, 0, 60, 20, []() {}));\nmenuBar->addElement(new UIButton(\"View\", 2, 0, 0, 60, 20, []() {}));\n\naddEntity(menuBar);\n
    "},{"location":"manual/game_development/user_interface/#uigridlayout","title":"UIGridLayout","text":"

    Organize elements in a grid (matrix):

    #include <graphics/ui/UIGridLayout.h>\n\n// Create grid layout (inventory)\npixelroot32::graphics::ui::UIGridLayout* inventory = new pixelroot32::graphics::ui::UIGridLayout(\n    10,   // x\n    60,   // y\n    220,  // width\n    160   // height\n);\n\ninventory->setColumns(4);  // 4 columns\ninventory->setPadding(5);\ninventory->setSpacing(4);\ninventory->setNavigationButtons(0, 1, 2, 3); // UP, DOWN, LEFT, RIGHT\n\n// Add items (automatically arranged in grid)\nfor (int i = 0; i < 16; i++) {\n    UIButton* item = new UIButton(\n        \"Item \" + std::to_string(i),\n        i,\n        0, 0,  // Position ignored\n        50, 50,\n        [i]() { useItem(i); }\n    );\n    inventory->addElement(item);\n}\n\naddEntity(inventory);\n
    "},{"location":"manual/game_development/user_interface/#uianchorlayout","title":"UIAnchorLayout","text":"

    Position elements at fixed screen positions (perfect for HUDs):

    #include <graphics/ui/UIAnchorLayout.h>\n\n// Create anchor layout for HUD\npixelroot32::graphics::ui::UIAnchorLayout* hud = new pixelroot32::graphics::ui::UIAnchorLayout(\n    0,    // x\n    0,    // y\n    240,  // screen width\n    240   // screen height\n);\n\nhud->setScreenSize(240, 240);\n\n// Add HUD elements at different anchor points\nUILabel* scoreLabel = new UILabel(\"Score: 0\", 0, 0, Color::White, 1);\nUILabel* livesLabel = new UILabel(\"Lives: 3\", 0, 0, Color::White, 1);\n\nhud->addElement(scoreLabel, pixelroot32::graphics::ui::Anchor::TOP_LEFT);\nhud->addElement(livesLabel, pixelroot32::graphics::ui::Anchor::TOP_RIGHT);\n\naddEntity(hud);\n

    Available Anchors: - TOP_LEFT, TOP_RIGHT, TOP_CENTER - BOTTOM_LEFT, BOTTOM_RIGHT, BOTTOM_CENTER - LEFT_CENTER, RIGHT_CENTER - CENTER

    "},{"location":"manual/game_development/user_interface/#uipaddingcontainer","title":"UIPaddingContainer","text":"

    Add padding around a single element:

    #include <graphics/ui/UIPaddingContainer.h>\n\n// Create padding container\npixelroot32::graphics::ui::UIPaddingContainer* container = new pixelroot32::graphics::ui::UIPaddingContainer(\n    10,   // x\n    10,   // y\n    200,  // width\n    100   // height\n);\n\n// Set uniform padding\ncontainer->setPadding(10);\n\n// Or set asymmetric padding\ncontainer->setPadding(5, 15, 10, 10); // left, right, top, bottom\n\n// Add child element\ncontainer->setChild(button);\n\naddEntity(container);\n
    "},{"location":"manual/game_development/user_interface/#navigation","title":"Navigation","text":"

    Layouts handle D-pad navigation automatically:

    "},{"location":"manual/game_development/user_interface/#vertical-navigation","title":"Vertical Navigation","text":"
    verticalLayout->setNavigationButtons(Buttons::UP, Buttons::DOWN);\n\n// Layout automatically:\n// - Highlights selected button\n// - Scrolls to keep selected button visible\n// - Handles wrapping (optional)\n
    "},{"location":"manual/game_development/user_interface/#horizontal-navigation","title":"Horizontal Navigation","text":"
    horizontalLayout->setNavigationButtons(Buttons::LEFT, Buttons::RIGHT);\n
    "},{"location":"manual/game_development/user_interface/#grid-navigation","title":"Grid Navigation","text":"
    gridLayout->setNavigationButtons(Buttons::UP, Buttons::DOWN, Buttons::LEFT, Buttons::RIGHT);\n\n// Layout automatically:\n// - Handles 4-direction navigation\n// - Wraps around edges\n// - Updates selection\n
    "},{"location":"manual/game_development/user_interface/#manual-selection","title":"Manual Selection","text":"
    // Set selected element programmatically\nlayout->setSelectedIndex(2);\n\n// Get selected element\nint selected = layout->getSelectedIndex();\nUIElement* element = layout->getSelectedElement();\n
    "},{"location":"manual/game_development/user_interface/#complete-example-main-menu","title":"Complete Example: Main Menu","text":"
    #include <core/Scene.h>\n#include <graphics/ui/UIVerticalLayout.h>\n#include <graphics/ui/UIButton.h>\n#include <graphics/ui/UILabel.h>\n#include <graphics/ui/UIPanel.h>\n\nclass MainMenuScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIVerticalLayout* menuLayout;\n    pixelroot32::graphics::ui::UIPanel* menuPanel;\n\npublic:\n    void init() override {\n        // Create panel\n        menuPanel = new pixelroot32::graphics::ui::UIPanel(40, 40, 160, 160);\n        menuPanel->setBackgroundColor(pixelroot32::graphics::Color::Black);\n        menuPanel->setBorderColor(pixelroot32::graphics::Color::White);\n        menuPanel->setBorderWidth(2);\n        menuPanel->setRenderLayer(2);\n        addEntity(menuPanel);\n\n        // Create layout inside panel\n        menuLayout = new pixelroot32::graphics::ui::UIVerticalLayout(0, 0, 160, 160);\n        menuLayout->setPadding(10);\n        menuLayout->setSpacing(8);\n        menuLayout->setNavigationButtons(0, 1);\n        menuLayout->setButtonStyle(\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Cyan,\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Black\n        );\n\n        // Add title\n        pixelroot32::graphics::ui::UILabel* title = new pixelroot32::graphics::ui::UILabel(\n            \"GAME MENU\", 0, 0, pixelroot32::graphics::Color::Yellow, 2\n        );\n        menuLayout->addElement(title);\n\n        // Add menu buttons\n        menuLayout->addElement(new pixelroot32::graphics::ui::UIButton(\n            \"Start Game\", 0, 0, 0, 140, 25, []() { startGame(); }\n        ));\n\n        menuLayout->addElement(new pixelroot32::graphics::ui::UIButton(\n            \"Options\", 1, 0, 0, 140, 25, []() { showOptions(); }\n        ));\n\n        menuLayout->addElement(new pixelroot32::graphics::ui::UIButton(\n            \"Quit\", 2, 0, 0, 140, 25, []() { quitGame(); }\n        ));\n\n        menuPanel->setChild(menuLayout);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Handle input for layout navigation\n        auto& input = engine.getInputManager();\n        menuLayout->handleInput(input);\n\n        Scene::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/game_development/user_interface/#complete-example-hud","title":"Complete Example: HUD","text":"
    class GameHUD : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::ui::UIAnchorLayout* hud;\n    pixelroot32::graphics::ui::UILabel* scoreLabel;\n    pixelroot32::graphics::ui::UILabel* livesLabel;\n    pixelroot32::graphics::ui::UILabel* healthLabel;\n\npublic:\n    GameHUD()\n        : Entity(0, 0, 240, 240, pixelroot32::core::EntityType::UI_ELEMENT) {\n        setRenderLayer(2); // UI layer\n\n        // Create HUD layout\n        hud = new pixelroot32::graphics::ui::UIAnchorLayout(0, 0, 240, 240);\n        hud->setScreenSize(240, 240);\n\n        // Create labels\n        scoreLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Score: 0\", 0, 0, pixelroot32::graphics::Color::White, 1\n        );\n\n        livesLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Lives: 3\", 0, 0, pixelroot32::graphics::Color::White, 1\n        );\n\n        healthLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Health: 100%\", 0, 0, pixelroot32::graphics::Color::Green, 1\n        );\n\n        // Position labels\n        hud->addElement(scoreLabel, pixelroot32::graphics::ui::Anchor::TOP_LEFT);\n        hud->addElement(livesLabel, pixelroot32::graphics::ui::Anchor::TOP_RIGHT);\n        hud->addElement(healthLabel, pixelroot32::graphics::ui::Anchor::BOTTOM_CENTER);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Update HUD text (example)\n        char buffer[32];\n        snprintf(buffer, sizeof(buffer), \"Score: %d\", currentScore);\n        scoreLabel->setText(buffer);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // HUD draws itself through its layout\n    }\n\n    // Add HUD to scene\n    void addToScene(Scene* scene) {\n        scene->addEntity(hud);\n    }\n};\n
    "},{"location":"manual/game_development/user_interface/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/user_interface/#organization","title":"Organization","text":"
    • Use render layer 2: Keep all UI on the top layer
    • Group related elements: Use panels to group menu items
    • Separate HUD from menus: Use different layouts for different UI types
    • Reuse layouts: Create layout factories for common patterns
    "},{"location":"manual/game_development/user_interface/#performance","title":"Performance","text":"
    • Layouts use viewport culling: Only visible elements are rendered
    • Minimize text updates: Updating text has overhead
    • Use appropriate layouts: Choose the right layout for your needs
    • Limit element count: Too many elements can impact performance
    "},{"location":"manual/game_development/user_interface/#navigation_1","title":"Navigation","text":"
    • Set navigation buttons: Configure D-pad navigation for layouts
    • Handle input in update(): Check for button presses to trigger actions
    • Provide visual feedback: Selected buttons should be clearly visible
    • Test navigation flow: Ensure navigation feels responsive
    "},{"location":"manual/game_development/user_interface/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/user_interface/#menu-system","title":"Menu System","text":"
    class MenuSystem {\n    UIVerticalLayout* currentMenu;\n\npublic:\n    void showMainMenu() {\n        currentMenu = createMainMenu();\n        scene->addEntity(currentMenu);\n    }\n\n    void showPauseMenu() {\n        currentMenu = createPauseMenu();\n        scene->addEntity(currentMenu);\n    }\n\n    void hideMenu() {\n        if (currentMenu) {\n            scene->removeEntity(currentMenu);\n            currentMenu = nullptr;\n        }\n    }\n};\n
    "},{"location":"manual/game_development/user_interface/#dynamic-ui-updates","title":"Dynamic UI Updates","text":"
    void updateHUD(int score, int lives, int health) {\n    char buffer[32];\n\n    snprintf(buffer, sizeof(buffer), \"Score: %d\", score);\n    scoreLabel->setText(buffer);\n\n    snprintf(buffer, sizeof(buffer), \"Lives: %d\", lives);\n    livesLabel->setText(buffer);\n\n    snprintf(buffer, sizeof(buffer), \"Health: %d%%\", health);\n    healthLabel->setText(buffer);\n}\n
    "},{"location":"manual/game_development/user_interface/#next-steps","title":"Next Steps","text":"

    Now that you understand the UI system, you've completed the core game development topics. Continue with: - Advanced Graphics - Advanced sprite techniques - Camera and Scrolling - Create scrolling levels - Performance Tuning - Improve performance

    See also: - API Reference - UIElement - API Reference - UIButton - API Reference - UI Layouts - Manual - UI Overview

    "},{"location":"manual/optimization/extensibility/","title":"Extensibility","text":"

    PixelRoot32 is designed to be extensible. This guide covers how to create custom drivers, audio backends, and extend existing systems.

    "},{"location":"manual/optimization/extensibility/#creating-custom-display-drivers","title":"Creating Custom Display Drivers","text":"

    To support a new display, implement the DrawSurface interface.

    "},{"location":"manual/optimization/extensibility/#drawsurface-interface","title":"DrawSurface Interface","text":"
    #include <graphics/DrawSurface.h>\n\nclass MyCustomDrawer : public pixelroot32::graphics::DrawSurface {\npublic:\n    // Required methods\n    void init() override;\n    void setRotation(uint8_t rotation) override;\n    void clearBuffer() override;\n    void sendBuffer() override;\n\n    // Drawing primitives\n    void drawPixel(int x, int y, uint16_t color) override;\n    void drawLine(int x1, int y1, int x2, int y2, uint16_t color) override;\n    void drawRectangle(int x, int y, int width, int height, uint16_t color) override;\n    void drawFilledRectangle(int x, int y, int width, int height, uint16_t color) override;\n    void drawCircle(int x, int y, int radius, uint16_t color) override;\n    void drawFilledCircle(int x, int y, int radius, uint16_t color) override;\n    void drawBitmap(int x, int y, int width, int height, const uint8_t* bitmap, uint16_t color) override;\n\n    // Text (deprecated, but must implement)\n    void drawText(const char* text, int16_t x, int16_t y, uint16_t color, uint8_t size) override;\n    void drawTextCentered(const char* text, int16_t y, uint16_t color, uint8_t size) override;\n\n    // State management\n    void setTextColor(uint16_t color) override;\n    void setTextSize(uint8_t size) override;\n    void setCursor(int16_t x, int16_t y) override;\n    void setContrast(uint8_t level) override;\n    void setDisplaySize(int w, int h) override;\n\n    // Utilities\n    uint16_t color565(uint8_t r, uint8_t g, uint8_t b) override;\n    bool processEvents() override;\n    void present() override;\n};\n
    "},{"location":"manual/optimization/extensibility/#example-simple-custom-drawer","title":"Example: Simple Custom Drawer","text":"
    #include <graphics/DrawSurface.h>\n\nclass SimpleDrawer : public pixelroot32::graphics::DrawSurface {\nprivate:\n    uint16_t* framebuffer;\n    int width, height;\n\npublic:\n    SimpleDrawer(int w, int h) : width(w), height(h) {\n        framebuffer = new uint16_t[w * h];\n    }\n\n    ~SimpleDrawer() {\n        delete[] framebuffer;\n    }\n\n    void init() override {\n        // Initialize your display hardware\n        // Clear framebuffer\n        clearBuffer();\n    }\n\n    void clearBuffer() override {\n        for (int i = 0; i < width * height; i++) {\n            framebuffer[i] = 0x0000; // Black\n        }\n    }\n\n    void sendBuffer() override {\n        // Send framebuffer to display\n        // Implementation depends on your hardware\n    }\n\n    void drawPixel(int x, int y, uint16_t color) override {\n        if (x >= 0 && x < width && y >= 0 && y < height) {\n            framebuffer[y * width + x] = color;\n        }\n    }\n\n    void drawFilledRectangle(int x, int y, int w, int h, uint16_t color) override {\n        for (int py = y; py < y + h; py++) {\n            for (int px = x; px < x + w; px++) {\n                drawPixel(px, py, color);\n            }\n        }\n    }\n\n    // Implement other required methods...\n    // (See TFT_eSPI_Drawer or SDL2_Drawer for reference implementations)\n};\n
    "},{"location":"manual/optimization/extensibility/#integrating-custom-driver","title":"Integrating Custom Driver","text":"
    // Create custom drawer\nSimpleDrawer* customDrawer = new SimpleDrawer(240, 240);\n\n// Create renderer with custom drawer\npixelroot32::graphics::DisplayConfig config(\n    pixelroot32::graphics::DisplayType::NONE, // Use NONE for custom\n    0, 240, 240\n);\n\n// You'll need to modify Engine to accept custom DrawSurface\n// Or create a custom Engine wrapper\n
    "},{"location":"manual/optimization/extensibility/#creating-custom-audio-backends","title":"Creating Custom Audio Backends","text":"

    Implement the AudioBackend interface for custom audio hardware.

    "},{"location":"manual/optimization/extensibility/#audiobackend-interface","title":"AudioBackend Interface","text":"
    #include <audio/AudioBackend.h>\n\nclass MyCustomAudioBackend : public pixelroot32::audio::AudioBackend {\npublic:\n    // Required methods\n    void init() override;\n    void start() override;\n    void stop() override;\n    uint32_t getSampleRate() const override;\n\n    // Audio generation\n    int16_t generateSample() override;\n\n    // Channel management\n    void setChannelWave(int channel, pixelroot32::audio::WaveType type, float frequency, float duty) override;\n    void setChannelVolume(int channel, float volume) override;\n    void stopChannel(int channel) override;\n};\n
    "},{"location":"manual/optimization/extensibility/#example-custom-audio-backend","title":"Example: Custom Audio Backend","text":"
    #include <audio/AudioBackend.h>\n\nclass CustomAudioBackend : public pixelroot32::audio::AudioBackend {\nprivate:\n    uint32_t sampleRate;\n    float phase[4] = {0, 0, 0, 0}; // 4 channels\n    float frequency[4] = {0, 0, 0, 0};\n    float volume[4] = {0, 0, 0, 0};\n    pixelroot32::audio::WaveType waveType[4];\n\npublic:\n    CustomAudioBackend(uint32_t rate) : sampleRate(rate) {\n        for (int i = 0; i < 4; i++) {\n            waveType[i] = pixelroot32::audio::WaveType::PULSE;\n            volume[i] = 0.0f;\n        }\n    }\n\n    void init() override {\n        // Initialize your audio hardware\n    }\n\n    void start() override {\n        // Start audio output\n    }\n\n    void stop() override {\n        // Stop audio output\n    }\n\n    uint32_t getSampleRate() const override {\n        return sampleRate;\n    }\n\n    int16_t generateSample() override {\n        float sample = 0.0f;\n\n        for (int ch = 0; ch < 4; ch++) {\n            if (frequency[ch] > 0 && volume[ch] > 0) {\n                float phaseIncrement = frequency[ch] / sampleRate;\n                phase[ch] += phaseIncrement;\n                if (phase[ch] >= 1.0f) phase[ch] -= 1.0f;\n\n                float channelSample = 0.0f;\n                switch (waveType[ch]) {\n                    case pixelroot32::audio::WaveType::PULSE:\n                        channelSample = (phase[ch] < 0.5f) ? 1.0f : -1.0f;\n                        break;\n                    case pixelroot32::audio::WaveType::TRIANGLE:\n                        channelSample = (phase[ch] < 0.5f) ? \n                            (phase[ch] * 4.0f - 1.0f) : \n                            (3.0f - phase[ch] * 4.0f);\n                        break;\n                    // ... other wave types\n                }\n\n                sample += channelSample * volume[ch];\n            }\n        }\n\n        // Clamp and convert to int16_t\n        if (sample > 1.0f) sample = 1.0f;\n        if (sample < -1.0f) sample = -1.0f;\n        return static_cast<int16_t>(sample * 32767.0f);\n    }\n\n    void setChannelWave(int ch, pixelroot32::audio::WaveType type, \n                       float freq, float duty) override {\n        if (ch >= 0 && ch < 4) {\n            waveType[ch] = type;\n            frequency[ch] = freq;\n        }\n    }\n\n    void setChannelVolume(int ch, float vol) override {\n        if (ch >= 0 && ch < 4) {\n            volume[ch] = vol;\n        }\n    }\n\n    void stopChannel(int ch) override {\n        if (ch >= 0 && ch < 4) {\n            frequency[ch] = 0.0f;\n            volume[ch] = 0.0f;\n        }\n    }\n};\n
    "},{"location":"manual/optimization/extensibility/#extending-existing-systems","title":"Extending Existing Systems","text":""},{"location":"manual/optimization/extensibility/#custom-entity-types","title":"Custom Entity Types","text":"

    Create specialized entity types:

    class PowerUpActor : public pixelroot32::core::Actor {\nprivate:\n    PowerUpType type;\n    float lifetime = 5.0f; // 5 seconds\n\npublic:\n    PowerUpActor(float x, float y, PowerUpType t)\n        : Actor(x, y, 8, 8), type(t) {\n        setRenderLayer(1);\n        setCollisionLayer(Layers::POWERUP);\n        setCollisionMask(Layers::PLAYER);\n    }\n\n    void update(unsigned long deltaTime) override {\n        lifetime -= deltaTime * 0.001f;\n        if (lifetime <= 0) {\n            isEnabled = false;\n            isVisible = false;\n        }\n\n        // Animate (bob up and down)\n        y += sin(millis() * 0.005f) * 0.5f;\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        if (other->isInLayer(Layers::PLAYER)) {\n            applyPowerUp(other);\n            isEnabled = false;\n            isVisible = false;\n        }\n    }\n\nprivate:\n    void applyPowerUp(pixelroot32::core::Actor* player) {\n        switch (type) {\n            case PowerUpType::SPEED:\n                // Increase player speed\n                break;\n            case PowerUpType::HEALTH:\n                // Restore health\n                break;\n            // ...\n        }\n    }\n};\n
    "},{"location":"manual/optimization/extensibility/#custom-ui-layouts","title":"Custom UI Layouts","text":"

    Create new layout types:

    #include <graphics/ui/UILayout.h>\n\nclass UICircularLayout : public pixelroot32::graphics::ui::UILayout {\nprivate:\n    float radius;\n    float startAngle;\n\npublic:\n    UICircularLayout(float x, float y, float w, float h, float r)\n        : UILayout(x, y, w, h), radius(r), startAngle(0.0f) {\n    }\n\n    void updateLayout() override {\n        int count = elements.size();\n        float angleStep = 360.0f / count;\n\n        for (size_t i = 0; i < elements.size(); i++) {\n            float angle = startAngle + (i * angleStep);\n            float rad = angle * M_PI / 180.0f;\n\n            float elementX = x + (radius * cos(rad)) - (elements[i]->width / 2);\n            float elementY = y + (radius * sin(rad)) - (elements[i]->height / 2);\n\n            elements[i]->x = elementX;\n            elements[i]->y = elementY;\n        }\n    }\n\n    void handleInput(const pixelroot32::input::InputManager& input) override {\n        // Implement circular navigation\n        // ...\n    }\n};\n
    "},{"location":"manual/optimization/extensibility/#custom-collision-primitives","title":"Custom Collision Primitives","text":"

    Extend collision system with new shapes:

    // Add to your game code (not engine modification)\nstruct Triangle {\n    float x1, y1, x2, y2, x3, y3;\n};\n\nbool intersects(const Triangle& tri, const pixelroot32::core::Rect& rect) {\n    // Implement triangle-rectangle intersection\n    // ...\n    return false;\n}\n\nbool intersects(const Triangle& tri1, const Triangle& tri2) {\n    // Implement triangle-triangle intersection\n    // ...\n    return false;\n}\n
    "},{"location":"manual/optimization/extensibility/#best-practices","title":"Best Practices","text":""},{"location":"manual/optimization/extensibility/#maintain-compatibility","title":"Maintain Compatibility","text":"
    • Don't break existing APIs: Extend, don't modify
    • Use inheritance: Inherit from base classes
    • Follow patterns: Match existing code patterns
    • Document extensions: Comment your custom code
    "},{"location":"manual/optimization/extensibility/#testing","title":"Testing","text":"
    • Test on both platforms: ESP32 and Native
    • Test edge cases: Boundary conditions, null pointers
    • Performance testing: Ensure extensions don't hurt performance
    • Memory testing: Check for leaks with custom code
    "},{"location":"manual/optimization/extensibility/#documentation","title":"Documentation","text":"
    • Comment your code: Explain why, not just what
    • Provide examples: Show how to use your extensions
    • Document limitations: State what doesn't work
    • Version compatibility: Note which engine version
    "},{"location":"manual/optimization/extensibility/#common-extension-patterns","title":"Common Extension Patterns","text":""},{"location":"manual/optimization/extensibility/#factory-pattern","title":"Factory Pattern","text":"
    class EntityFactory {\npublic:\n    static pixelroot32::core::Entity* createEnemy(EnemyType type, float x, float y) {\n        switch (type) {\n            case EnemyType::BASIC:\n                return new BasicEnemy(x, y);\n            case EnemyType::FAST:\n                return new FastEnemy(x, y);\n            case EnemyType::TANK:\n                return new TankEnemy(x, y);\n            default:\n                return nullptr;\n        }\n    }\n\n    static pixelroot32::core::Entity* createPowerUp(PowerUpType type, float x, float y) {\n        return new PowerUpActor(x, y, type);\n    }\n};\n
    "},{"location":"manual/optimization/extensibility/#strategy-pattern","title":"Strategy Pattern","text":"
    class MovementStrategy {\npublic:\n    virtual void update(pixelroot32::core::Actor* actor, unsigned long deltaTime) = 0;\n};\n\nclass LinearMovement : public MovementStrategy {\npublic:\n    void update(pixelroot32::core::Actor* actor, unsigned long deltaTime) override {\n        actor->x += speed * (deltaTime * 0.001f);\n    }\n};\n\nclass CircularMovement : public MovementStrategy {\npublic:\n    void update(pixelroot32::core::Actor* actor, unsigned long deltaTime) override {\n        float angle = millis() * 0.001f;\n        actor->x = centerX + radius * cos(angle);\n        actor->y = centerY + radius * sin(angle);\n    }\n};\n\nclass SmartEnemy : public pixelroot32::core::Actor {\nprivate:\n    MovementStrategy* movement;\n\npublic:\n    void setMovement(MovementStrategy* strat) {\n        movement = strat;\n    }\n\n    void update(unsigned long deltaTime) override {\n        if (movement) {\n            movement->update(this, deltaTime);\n        }\n    }\n};\n
    "},{"location":"manual/optimization/extensibility/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/optimization/extensibility/#driver-not-working","title":"Driver Not Working","text":"
    • Verify all interface methods are implemented
    • Check initialization order
    • Test with simple drawing first
    • Verify hardware connections
    "},{"location":"manual/optimization/extensibility/#audio-backend-issues","title":"Audio Backend Issues","text":"
    • Check sample rate matches hardware
    • Verify audio generation logic
    • Test with simple tones first
    • Check channel management
    "},{"location":"manual/optimization/extensibility/#extension-conflicts","title":"Extension Conflicts","text":"
    • Ensure namespace isolation
    • Avoid modifying engine code directly
    • Use composition over modification
    • Test with engine updates
    "},{"location":"manual/optimization/extensibility/#next-steps","title":"Next Steps","text":"

    Now that you understand extensibility, you've completed the optimization section. Continue with: - API Reference - Complete API documentation - Examples - Code examples - Resources - Tools and troubleshooting

    See also: - API Reference - DrawSurface - API Reference - AudioBackend - Manual - Platforms and Drivers

    "},{"location":"manual/optimization/memory_management/","title":"Memory Management","text":"

    ESP32 has limited memory, so efficient memory management is crucial for PixelRoot32 games. This guide covers memory constraints, object pooling, and best practices.

    "},{"location":"manual/optimization/memory_management/#esp32-memory-constraints","title":"ESP32 Memory Constraints","text":""},{"location":"manual/optimization/memory_management/#available-memory","title":"Available Memory","text":"

    ESP32 typically has: - RAM: ~320KB total (varies by model) - Flash: 4MB+ (for program storage) - Heap: Limited and fragmented over time

    "},{"location":"manual/optimization/memory_management/#real-world-limits","title":"Real-World Limits","text":"
    • MAX_ENTITIES: 32 per scene (hard limit)
    • Sprite data: Stored in flash (const/constexpr)
    • Dynamic allocation: Should be avoided in game loop
    • Stack: Limited (~8KB), avoid large stack allocations
    "},{"location":"manual/optimization/memory_management/#object-pooling","title":"Object Pooling","text":"

    Object pooling reuses objects instead of creating/destroying them, avoiding memory fragmentation.

    "},{"location":"manual/optimization/memory_management/#basic-pool-pattern","title":"Basic Pool Pattern","text":"
    class ProjectilePool {\nprivate:\n    static const int POOL_SIZE = 10;\n    ProjectileActor pool[POOL_SIZE];\n    bool inUse[POOL_SIZE];\n\npublic:\n    ProjectilePool() {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            inUse[i] = false;\n        }\n    }\n\n    ProjectileActor* getAvailable() {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (!inUse[i]) {\n                inUse[i] = true;\n                return &pool[i];\n            }\n        }\n        return nullptr; // Pool exhausted\n    }\n\n    void release(ProjectileActor* projectile) {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (&pool[i] == projectile) {\n                inUse[i] = false;\n                projectile->isEnabled = false;\n                projectile->isVisible = false;\n                break;\n            }\n        }\n    }\n};\n
    "},{"location":"manual/optimization/memory_management/#using-object-pools","title":"Using Object Pools","text":"
    class GameScene : public pixelroot32::core::Scene {\nprivate:\n    ProjectilePool projectilePool;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Fire projectile\n        if (input.isButtonPressed(Buttons::A)) {\n            ProjectileActor* proj = projectilePool.getAvailable();\n            if (proj) {\n                proj->x = player->x;\n                proj->y = player->y;\n                proj->isEnabled = true;\n                proj->isVisible = true;\n                // ... initialize projectile\n            }\n        }\n\n        // Clean up projectiles that hit target\n        for (auto* entity : entities) {\n            if (auto* proj = dynamic_cast<ProjectileActor*>(entity)) {\n                if (proj->hitTarget) {\n                    projectilePool.release(proj);\n                }\n            }\n        }\n    }\n};\n
    "},{"location":"manual/optimization/memory_management/#complete-example-entity-pool","title":"Complete Example: Entity Pool","text":"
    template<typename T, int POOL_SIZE>\nclass EntityPool {\nprivate:\n    T pool[POOL_SIZE];\n    bool inUse[POOL_SIZE];\n    int activeCount = 0;\n\npublic:\n    EntityPool() {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            inUse[i] = false;\n        }\n    }\n\n    T* acquire() {\n        if (activeCount >= POOL_SIZE) {\n            return nullptr; // Pool full\n        }\n\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (!inUse[i]) {\n                inUse[i] = true;\n                activeCount++;\n                return &pool[i];\n            }\n        }\n        return nullptr;\n    }\n\n    void release(T* obj) {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (&pool[i] == obj) {\n                inUse[i] = false;\n                activeCount--;\n                obj->isEnabled = false;\n                obj->isVisible = false;\n                break;\n            }\n        }\n    }\n\n    int getActiveCount() const { return activeCount; }\n    int getAvailableCount() const { return POOL_SIZE - activeCount; }\n};\n\n// Usage\nEntityPool<EnemyActor, 8> enemyPool;\nEntityPool<ParticleEmitter, 5> particlePool;\n
    "},{"location":"manual/optimization/memory_management/#scene-arena-experimental","title":"Scene Arena (Experimental)","text":"

    Scene Arena provides a memory arena for scene-specific allocations, reducing fragmentation.

    "},{"location":"manual/optimization/memory_management/#what-is-scene-arena","title":"What is Scene Arena?","text":"

    Scene Arena is a contiguous memory block pre-allocated for a scene. All scene entities are allocated from this arena instead of the heap.

    "},{"location":"manual/optimization/memory_management/#when-to-use","title":"When to Use","text":"
    • Large scenes: Scenes with many entities
    • Frequent allocation: Scenes that create/destroy entities often
    • Memory fragmentation: When heap fragmentation is a problem
    • Performance: When you need predictable allocation performance
    "},{"location":"manual/optimization/memory_management/#configuration","title":"Configuration","text":"
    #ifdef PIXELROOT32_ENABLE_SCENE_ARENA\n#include <core/Scene.h>\n\n// Define arena buffer (typically in scene header)\nstatic unsigned char MY_SCENE_ARENA_BUFFER[8192]; // 8KB arena\n\nclass MyScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Initialize arena\n        arena.init(MY_SCENE_ARENA_BUFFER, sizeof(MY_SCENE_ARENA_BUFFER));\n\n        // Now entities allocated with arena will use this memory\n        // (Requires custom allocation functions)\n    }\n};\n#endif\n
    "},{"location":"manual/optimization/memory_management/#limitations","title":"Limitations","text":"
    • Experimental: May have bugs or limitations
    • Fixed size: Arena size must be determined at compile time
    • No reallocation: Can't resize arena at runtime
    • Manual management: Requires careful memory management

    Note: Scene Arena is an experimental feature. Use object pooling for most cases.

    "},{"location":"manual/optimization/memory_management/#best-practices","title":"Best Practices","text":""},{"location":"manual/optimization/memory_management/#avoid-dynamic-allocation-in-game-loop","title":"Avoid Dynamic Allocation in Game Loop","text":"
    // \u274c BAD: Allocates every frame\nvoid update(unsigned long deltaTime) override {\n    if (shouldSpawnEnemy) {\n        EnemyActor* enemy = new EnemyActor(x, y);\n        addEntity(enemy);\n    }\n}\n\n// \u2705 GOOD: Use pool\nvoid update(unsigned long deltaTime) override {\n    if (shouldSpawnEnemy) {\n        EnemyActor* enemy = enemyPool.getAvailable();\n        if (enemy) {\n            enemy->reset(x, y);\n            enemy->isEnabled = true;\n        }\n    }\n}\n
    "},{"location":"manual/optimization/memory_management/#pre-allocate-resources","title":"Pre-allocate Resources","text":"
    class GameScene : public pixelroot32::core::Scene {\nprivate:\n    // Pre-allocated pools\n    ProjectilePool projectiles;\n    EnemyPool enemies;\n    ParticlePool particles;\n\npublic:\n    void init() override {\n        // All pools created in constructor\n        // No allocation in init() or update()\n    }\n};\n
    "},{"location":"manual/optimization/memory_management/#reuse-objects","title":"Reuse Objects","text":"
    class EnemyActor : public pixelroot32::core::Actor {\npublic:\n    void reset(float x, float y) {\n        this->x = x;\n        this->y = y;\n        this->isEnabled = true;\n        this->isVisible = true;\n        this->health = maxHealth;\n        // Reset all state\n    }\n\n    void deactivate() {\n        isEnabled = false;\n        isVisible = false;\n    }\n};\n\n// Usage\nEnemyActor* enemy = enemyPool.getAvailable();\nif (enemy) {\n    enemy->reset(spawnX, spawnY);\n    addEntity(enemy);\n}\n
    "},{"location":"manual/optimization/memory_management/#avoid-strings-and-dynamic-memory","title":"Avoid Strings and Dynamic Memory","text":"
    // \u274c BAD: String allocation\nvoid draw(Renderer& renderer) override {\n    std::string scoreText = \"Score: \" + std::to_string(score);\n    renderer.drawText(scoreText.c_str(), 10, 10, Color::White, 1);\n}\n\n// \u2705 GOOD: Static buffer\nvoid draw(Renderer& renderer) override {\n    char scoreBuffer[32];\n    snprintf(scoreBuffer, sizeof(scoreBuffer), \"Score: %d\", score);\n    renderer.drawText(scoreBuffer, 10, 10, Color::White, 1);\n}\n
    "},{"location":"manual/optimization/memory_management/#store-data-in-flash","title":"Store Data in Flash","text":"
    // \u2705 GOOD: Stored in flash (const/constexpr)\nstatic const uint16_t SPRITE_DATA[] = {\n    0b00111100,\n    0b01111110,\n    // ...\n};\n\n// \u274c BAD: Stored in RAM\nuint16_t spriteData[] = {\n    0b00111100,\n    0b01111110,\n    // ...\n};\n
    "},{"location":"manual/optimization/memory_management/#memory-monitoring","title":"Memory Monitoring","text":""},{"location":"manual/optimization/memory_management/#check-available-memory","title":"Check Available Memory","text":"
    #ifdef PLATFORM_ESP32\n#include <Arduino.h>\n\nvoid checkMemory() {\n    Serial.print(\"Free heap: \");\n    Serial.println(ESP.getFreeHeap());\n    Serial.print(\"Largest free block: \");\n    Serial.println(ESP.getMaxAllocHeap());\n}\n#endif\n
    "},{"location":"manual/optimization/memory_management/#monitor-entity-count","title":"Monitor Entity Count","text":"
    void update(unsigned long deltaTime) override {\n    Scene::update(deltaTime);\n\n    // Check entity count\n    int entityCount = getEntityCount();\n    if (entityCount >= MAX_ENTITIES) {\n        Serial.println(\"WARNING: Entity limit reached!\");\n    }\n}\n
    "},{"location":"manual/optimization/memory_management/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/optimization/memory_management/#entity-lifecycle-management","title":"Entity Lifecycle Management","text":"
    class ManagedEntity {\nprivate:\n    bool isActive = false;\n\npublic:\n    void activate(float x, float y) {\n        this->x = x;\n        this->y = y;\n        isActive = true;\n        isEnabled = true;\n        isVisible = true;\n    }\n\n    void deactivate() {\n        isActive = false;\n        isEnabled = false;\n        isVisible = false;\n    }\n\n    bool getIsActive() const { return isActive; }\n};\n\n// Pool manages lifecycle\nclass EntityManager {\nprivate:\n    EntityPool<ManagedEntity, 20> pool;\n\npublic:\n    ManagedEntity* spawn(float x, float y) {\n        auto* entity = pool.acquire();\n        if (entity) {\n            entity->activate(x, y);\n        }\n        return entity;\n    }\n\n    void despawn(ManagedEntity* entity) {\n        if (entity) {\n            entity->deactivate();\n            pool.release(entity);\n        }\n    }\n};\n
    "},{"location":"manual/optimization/memory_management/#memory-efficient-collections","title":"Memory-Efficient Collections","text":"
    // Fixed-size array instead of vector\nclass EntityArray {\nprivate:\n    static const int MAX_SIZE = 32;\n    pixelroot32::core::Entity* entities[MAX_SIZE];\n    int count = 0;\n\npublic:\n    bool add(pixelroot32::core::Entity* entity) {\n        if (count >= MAX_SIZE) return false;\n        entities[count++] = entity;\n        return true;\n    }\n\n    void remove(pixelroot32::core::Entity* entity) {\n        for (int i = 0; i < count; i++) {\n            if (entities[i] == entity) {\n                entities[i] = entities[--count];\n                break;\n            }\n        }\n    }\n\n    int size() const { return count; }\n    pixelroot32::core::Entity* operator[](int index) { return entities[index]; }\n};\n
    "},{"location":"manual/optimization/memory_management/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/optimization/memory_management/#out-of-memory-errors","title":"Out of Memory Errors","text":"
    • Reduce pool sizes
    • Use fewer entities
    • Store more data in flash
    • Avoid dynamic allocation
    • Check for memory leaks
    "},{"location":"manual/optimization/memory_management/#entity-limit-reached","title":"Entity Limit Reached","text":"
    • MAX_ENTITIES = 32 is a hard limit
    • Use object pooling to reuse entities
    • Deactivate entities instead of removing
    • Combine multiple entities into one
    "},{"location":"manual/optimization/memory_management/#memory-fragmentation","title":"Memory Fragmentation","text":"
    • Use object pooling
    • Pre-allocate all resources
    • Avoid frequent new/delete
    • Consider Scene Arena (experimental)
    "},{"location":"manual/optimization/memory_management/#next-steps","title":"Next Steps","text":"

    Now that you understand memory management, learn about: - Performance Optimization - Improve game performance - Platforms and Drivers - Understand platform specifics - Extensibility - Extend the engine

    See also: - API Reference - Scene - Manual - Scenes and Entities

    "},{"location":"manual/optimization/performance_tuning/","title":"Performance Optimization","text":"

    This guide covers techniques to improve game performance on ESP32, including rendering optimization, logic optimization, and profiling.

    "},{"location":"manual/optimization/performance_tuning/#esp32-performance-characteristics","title":"ESP32 Performance Characteristics","text":""},{"location":"manual/optimization/performance_tuning/#cpu-limitations","title":"CPU Limitations","text":"
    • Dual-core: 240MHz (typically)
    • Single-threaded game loop: One core handles everything
    • Target FPS: 30-60 FPS (depends on game complexity)
    • Frame budget: ~16-33ms per frame at 60 FPS
    "},{"location":"manual/optimization/performance_tuning/#common-bottlenecks","title":"Common Bottlenecks","text":"
    1. Rendering: Too many draw calls
    2. Collision detection: Too many collision checks
    3. Memory allocation: Dynamic allocation in game loop
    4. Complex calculations: Expensive math operations
    5. String operations: String concatenation/formatting
    "},{"location":"manual/optimization/performance_tuning/#tecnicas-de-optimizacion","title":"T\u00e9cnicas de Optimizaci\u00f3n","text":"

    El motor utiliza varias t\u00e9cnicas para maximizar los FPS, especialmente en hardware limitado como el ESP32.

    "},{"location":"manual/optimization/performance_tuning/#1-viewport-culling-recorte-de-camara","title":"1. Viewport Culling (Recorte de C\u00e1mara)","text":"

    No proceses objetos que est\u00e1n fuera de la pantalla. El motor lo hace autom\u00e1ticamente en drawTileMap, pero debes implementarlo en tu l\u00f3gica de actualizaci\u00f3n:

    bool isOnScreen(float x, float y, int width, int height, \n                const Camera2D& camera) {\n    float cameraX = camera.getX();\n    float cameraY = camera.getY();\n    int screenWidth = engine.getRenderer().getWidth();\n    int screenHeight = engine.getRenderer().getHeight();\n\n    return !(x + width < cameraX || \n             x > cameraX + screenWidth ||\n             y + height < cameraY || \n             y > cameraY + screenHeight);\n}\n
    "},{"location":"manual/optimization/performance_tuning/#2-optimizacion-de-memoria-y-cpu-esp32","title":"2. Optimizaci\u00f3n de Memoria y CPU (ESP32)","text":"

    Para la plataforma ESP32, se han implementado optimizaciones de bajo nivel cr\u00edticas:

    • IRAM_ATTR: Las funciones cr\u00edticas de renderizado (drawSprite, drawTileMap, etc.) est\u00e1n marcadas para ejecutarse desde la RAM interna (IRAM), eliminando la latencia de lectura de la Flash SPI.
    • DMA (Direct Memory Access): El volcado del buffer a la pantalla TFT se realiza mediante DMA, lo que permite que la CPU comience a procesar el siguiente frame mientras el hardware transfiere los datos.
    • Acceso a Datos de 16 bits: Los sprites de 2bpp y 4bpp utilizan punteros uint16_t* para garantizar accesos alineados a memoria, lo cual es significativamente m\u00e1s r\u00e1pido en la arquitectura Xtensa del ESP32.
    "},{"location":"manual/optimization/performance_tuning/#3-optimizacion-de-tilemaps","title":"3. Optimizaci\u00f3n de TileMaps","text":"

    El renderizado de mapas de tiles es una de las operaciones m\u00e1s costosas. PixelRoot32 utiliza:

    • Cach\u00e9 de Paleta: Durante el dibujado de un tilemap, se genera una tabla de b\u00fasqueda (LUT) temporal para evitar c\u00e1lculos de color redundantes por cada p\u00edxel.
    • Dibujado por Columnas: Optimizado para minimizar los saltos de memoria en el framebuffer.
    "},{"location":"manual/optimization/performance_tuning/#4-colisiones-eficientes","title":"4. Colisiones Eficientes","text":"

    Usa colisiones basadas en tiles siempre que sea posible. Acceder a un array de tiles es O(1), mientras que iterar sobre una lista de entidades es O(n).

    // Ejemplo de colisi\u00f3n r\u00e1pida con el mapa\nint tileX = x / 8;\nint tileY = y / 8;\nif (levelMap.data[tileY * levelMap.width + tileX] != 0) {\n    // Colisi\u00f3n detectada\n}\n
    "},{"location":"manual/optimization/performance_tuning/#recomendaciones-generales","title":"Recomendaciones Generales","text":"
    • Sprites Indexados: Prefiere Sprite2bpp (4 colores) o Sprite4bpp (16 colores) sobre Sprite (1bpp) si necesitas color, ya que est\u00e1n altamente optimizados.
    • Evitar std::string en el Loop: Las concatenaciones de strings generan fragmentaci\u00f3n de memoria. Usa buffers est\u00e1ticos o char[] para textos din\u00e1micos.
    • Perfilado: Utiliza engine.getFPS() para monitorear el impacto de tus cambios en tiempo real.
    "},{"location":"manual/optimization/performance_tuning/#common-optimization-patterns","title":"Common Optimization Patterns","text":""},{"location":"manual/optimization/performance_tuning/#update-frequency-reduction","title":"Update Frequency Reduction","text":"
    class LowFrequencyUpdater {\nprivate:\n    unsigned long timer = 0;\n    unsigned long interval = 100; // Update every 100ms\n\npublic:\n    void update(unsigned long deltaTime) {\n        timer += deltaTime;\n        if (timer >= interval) {\n            timer -= interval;\n            // Do expensive update\n            expensiveUpdate();\n        }\n    }\n};\n
    "},{"location":"manual/optimization/performance_tuning/#spatial-partitioning-simple","title":"Spatial Partitioning (Simple)","text":"
    // Divide screen into zones\nclass SpatialGrid {\nprivate:\n    static const int GRID_SIZE = 4;\n    static const int CELL_WIDTH = 60;\n    static const int CELL_HEIGHT = 60;\n\n    std::vector<Actor*> grid[GRID_SIZE][GRID_SIZE];\n\npublic:\n    void add(Actor* actor) {\n        int cellX = static_cast<int>(actor->x) / CELL_WIDTH;\n        int cellY = static_cast<int>(actor->y) / CELL_HEIGHT;\n        if (cellX >= 0 && cellX < GRID_SIZE && \n            cellY >= 0 && cellY < GRID_SIZE) {\n            grid[cellY][cellX].push_back(actor);\n        }\n    }\n\n    void checkCollisions() {\n        // Only check collisions within same cell\n        for (int y = 0; y < GRID_SIZE; y++) {\n            for (int x = 0; x < GRID_SIZE; x++) {\n                auto& cell = grid[y][x];\n                for (size_t i = 0; i < cell.size(); i++) {\n                    for (size_t j = i + 1; j < cell.size(); j++) {\n                        checkCollision(cell[i], cell[j]);\n                    }\n                }\n            }\n        }\n    }\n};\n
    "},{"location":"manual/optimization/performance_tuning/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/optimization/performance_tuning/#low-fps","title":"Low FPS","text":"
    • Profile to find bottlenecks
    • Reduce entity count
    • Optimize rendering (culling, batching)
    • Simplify collision detection
    • Reduce update frequency
    "},{"location":"manual/optimization/performance_tuning/#frame-drops","title":"Frame Drops","text":"
    • Check for expensive operations in update()
    • Avoid dynamic allocation
    • Cache calculations
    • Reduce draw calls
    "},{"location":"manual/optimization/performance_tuning/#stuttering","title":"Stuttering","text":"
    • Ensure frame-rate independence (use deltaTime)
    • Avoid blocking operations
    • Pre-load resources
    • Use object pooling
    "},{"location":"manual/optimization/performance_tuning/#next-steps","title":"Next Steps","text":"

    Now that you understand performance optimization, learn about: - Memory Management - Manage memory efficiently - Platforms and Drivers - Platform-specific optimizations - Extensibility - Extend the engine

    See also: - Manual - Basic Rendering - Manual - Physics and Collisions

    "},{"location":"manual/optimization/platforms_and_drivers/","title":"Platforms and Drivers","text":"

    PixelRoot32 supports multiple platforms through driver abstraction. This guide covers supported platforms, display drivers, audio backends, and build configuration.

    "},{"location":"manual/optimization/platforms_and_drivers/#supported-platforms","title":"Supported Platforms","text":""},{"location":"manual/optimization/platforms_and_drivers/#esp32","title":"ESP32","text":"

    Primary platform for PixelRoot32 games.

    Characteristics: - TFT_eSPI display driver - Internal DAC or I2S audio - GPIO button input - Limited RAM/Flash - Real hardware constraints

    Use for: - Final game deployment - Hardware testing - Production builds

    "},{"location":"manual/optimization/platforms_and_drivers/#nativedesktop-sdl2","title":"Native/Desktop (SDL2)","text":"

    Development platform for rapid iteration.

    Characteristics: - SDL2 display driver - SDL2 audio backend - Keyboard input - Unlimited resources (for testing) - Fast development cycle

    Use for: - Development and debugging - Testing without hardware - Rapid prototyping - CI/CD testing

    "},{"location":"manual/optimization/platforms_and_drivers/#display-drivers","title":"Display Drivers","text":""},{"location":"manual/optimization/platforms_and_drivers/#tft_espi-esp32","title":"TFT_eSPI (ESP32)","text":"

    TFT_eSPI is the display driver for ESP32, supporting many TFT displays.

    "},{"location":"manual/optimization/platforms_and_drivers/#optimizaciones-esp32","title":"Optimizaciones ESP32","text":"

    Para maximizar el rendimiento en ESP32, PixelRoot32 utiliza:

    • DMA (Direct Memory Access): Las transferencias al display se realizan en segundo plano, permitiendo que la CPU prepare el siguiente frame mientras se env\u00eda el actual.
    • Doble Buffer con IRAM: El motor utiliza un buffer de pantalla (Sprite de TFT_eSPI) optimizado para transferencias r\u00e1pidas.
    • Alineaci\u00f3n de 16 bits: Los datos de sprites 2bpp/4bpp est\u00e1n alineados a palabras de 16 bits para aprovechar la arquitectura Xtensa.
    • IRAM_ATTR: Las funciones cr\u00edticas de renderizado est\u00e1n marcadas para residir en la RAM de instrucciones, evitando cuellos de botella por acceso a la Flash.
    "},{"location":"manual/optimization/platforms_and_drivers/#configuracion-dma","title":"Configuraci\u00f3n DMA","text":"

    El DMA se activa autom\u00e1ticamente si el hardware lo soporta. Aseg\u00farate de configurar la frecuencia SPI adecuada para tu display (usualmente 40MHz u 80MHz).

    [env:esp32dev]\nbuild_flags = \n    -D ST7789_DRIVER          # Display type\n    -D TFT_WIDTH=240          # Display width\n    -D TFT_HEIGHT=240         # Display height\n    -D TFT_MOSI=23            # SPI MOSI pin\n    -D TFT_SCLK=18            # SPI clock pin\n    -D TFT_DC=2               # Data/Command pin\n    -D TFT_RST=4              # Reset pin\n    -D TFT_CS=-1              # Chip select (-1 if not used)\n    -D SPI_FREQUENCY=40000000 # SPI frequency\n
    "},{"location":"manual/optimization/platforms_and_drivers/#supported-displays","title":"Supported Displays","text":"
    • ST7735: 128x128, 128x160
    • ST7789: 240x240, 240x320
    • ILI9341: 240x320
    • And more: See TFT_eSPI documentation
    "},{"location":"manual/optimization/platforms_and_drivers/#usage","title":"Usage","text":"
    #include <drivers/esp32/TFT_eSPI_Drawer.h>\n\n// Display configuration\npixelroot32::graphics::DisplayConfig displayConfig(\n    pixelroot32::graphics::DisplayType::ST7789,\n    0,      // rotation\n    240,    // width\n    240     // height\n);\n\n// TFT_eSPI_Drawer is created automatically by Engine\n// No manual driver creation needed\n
    "},{"location":"manual/optimization/platforms_and_drivers/#sdl2_drawer-native","title":"SDL2_Drawer (Native)","text":"

    SDL2_Drawer provides display output for PC/desktop development.

    "},{"location":"manual/optimization/platforms_and_drivers/#configuration","title":"Configuration","text":"
    #include <drivers/native/SDL2_Drawer.h>\n\n// Display configuration (NONE defaults to SDL2)\npixelroot32::graphics::DisplayConfig displayConfig(\n    pixelroot32::graphics::DisplayType::NONE,\n    0,      // rotation\n    240,    // width\n    240     // height\n);\n\n// SDL2_Drawer is created automatically\n
    "},{"location":"manual/optimization/platforms_and_drivers/#sdl2-installation","title":"SDL2 Installation","text":"

    Windows (MSYS2):

    pacman -S mingw-w64-x86_64-SDL2\n

    Linux:

    sudo apt-get install libsdl2-dev\n

    macOS:

    brew install sdl2\n

    "},{"location":"manual/optimization/platforms_and_drivers/#audio-backends","title":"Audio Backends","text":""},{"location":"manual/optimization/platforms_and_drivers/#esp32_dac_audiobackend","title":"ESP32_DAC_AudioBackend","text":"

    Uses ESP32's internal DAC for audio output.

    "},{"location":"manual/optimization/platforms_and_drivers/#configuration_1","title":"Configuration","text":"
    #include <drivers/esp32/ESP32_DAC_AudioBackend.h>\n\nconst int DAC_PIN = 25; // GPIO 25 or 26\npixelroot32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(\n    DAC_PIN,    // DAC pin (25 or 26)\n    11025       // Sample rate (Hz)\n);\n\npixelroot32::audio::AudioConfig audioConfig(\n    &audioBackend, \n    audioBackend.getSampleRate()\n);\n

    Characteristics: - Simple setup (just one pin) - Lower quality than I2S - Good for basic audio - Sample rate: 11025 Hz recommended

    "},{"location":"manual/optimization/platforms_and_drivers/#esp32_i2s_audiobackend","title":"ESP32_I2S_AudioBackend","text":"

    Uses ESP32's I2S peripheral for higher quality audio.

    "},{"location":"manual/optimization/platforms_and_drivers/#configuration_2","title":"Configuration","text":"
    #include <drivers/esp32/ESP32_I2S_AudioBackend.h>\n\nconst int I2S_BCLK = 26;  // Bit clock pin\nconst int I2S_LRCK = 25;  // Left/Right clock pin\nconst int I2S_DOUT = 22;  // Data out pin\n\npixelroot32::drivers::esp32::ESP32_I2S_AudioBackend audioBackend(\n    I2S_BCLK,\n    I2S_LRCK,\n    I2S_DOUT,\n    22050  // Sample rate (Hz)\n);\n\npixelroot32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n

    Characteristics: - Higher quality than DAC - Requires external I2S DAC (e.g., MAX98357A) - Better for music - Sample rate: 22050 Hz recommended

    "},{"location":"manual/optimization/platforms_and_drivers/#sdl2_audiobackend-native","title":"SDL2_AudioBackend (Native)","text":"

    SDL2 audio for PC development.

    "},{"location":"manual/optimization/platforms_and_drivers/#configuration_3","title":"Configuration","text":"
    #include <drivers/native/SDL2_AudioBackend.h>\n\npixelroot32::drivers::native::SDL2_AudioBackend audioBackend(\n    22050,  // Sample rate\n    1024    // Buffer size\n);\n\npixelroot32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n
    "},{"location":"manual/optimization/platforms_and_drivers/#build-flags","title":"Build Flags","text":""},{"location":"manual/optimization/platforms_and_drivers/#experimental-features","title":"Experimental Features","text":"

    Enable experimental features with build flags:

    [env:esp32dev]\nbuild_flags = \n    -D PIXELROOT32_ENABLE_2BPP_SPRITES    # Enable 2bpp sprite format\n    -D PIXELROOT32_ENABLE_4BPP_SPRITES   # Enable 4bpp sprite format\n    -D PIXELROOT32_ENABLE_SCENE_ARENA    # Enable Scene Arena (experimental)\n
    "},{"location":"manual/optimization/platforms_and_drivers/#scene-limits-max_layers-max_entities","title":"Scene limits (MAX_LAYERS / MAX_ENTITIES)","text":"

    You can override the default scene limits from your project without modifying the engine. The default of 3 for MAX_LAYERS is due to ESP32 platform constraints (memory and draw-loop cost); on native/PC you can use a higher value.

    Option A: Compiler flags (recommended) \u2014 in platformio.ini, add to build_flags for your environment:

    build_flags =\n    -DMAX_LAYERS=5\n    -DMAX_ENTITIES=64\n

    The compiler defines these before any .cpp is processed. Because Scene.h uses #ifndef MAX_LAYERS / #ifndef MAX_ENTITIES, your values are used (more render layers drawn in Scene::draw, and on Arduino the entity queue capacity when built with MAX_ENTITIES).

    See API Reference - Scene - Overriding scene limits for details.

    "},{"location":"manual/optimization/platforms_and_drivers/#platform-detection","title":"Platform Detection","text":"
    #ifdef PLATFORM_ESP32\n    // ESP32-specific code\n    Serial.println(\"Running on ESP32\");\n#endif\n\n#ifdef PLATFORM_NATIVE\n    // Native/PC-specific code\n    printf(\"Running on PC\\n\");\n#endif\n
    "},{"location":"manual/optimization/platforms_and_drivers/#optimization-flags","title":"Optimization Flags","text":"
    [env:esp32dev]\nbuild_flags = \n    -O2              # Optimization level\n    -ffunction-sections\n    -fdata-sections\n    -Wl,--gc-sections\n
    "},{"location":"manual/optimization/platforms_and_drivers/#complete-platform-setup-examples","title":"Complete Platform Setup Examples","text":""},{"location":"manual/optimization/platforms_and_drivers/#esp32-complete-setup","title":"ESP32 Complete Setup","text":"
    #include <Arduino.h>\n#include <core/Engine.h>\n#include <drivers/esp32/TFT_eSPI_Drawer.h>\n#include <drivers/esp32/ESP32_DAC_AudioBackend.h>\n\nnamespace pr32 = pixelroot32;\n\n// Audio\nconst int DAC_PIN = 25;\npr32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(DAC_PIN, 11025);\n\n// Display\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::ST7789,\n    0, 240, 240\n);\n\n// Input\npr32::input::InputConfig inputConfig(\n    6, 32, 27, 33, 14, 13, 12  // 6 buttons, pins\n);\n\n// Audio config\npr32::audio::AudioConfig audioConfig(&audioBackend, 11025);\n\n// Engine\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\nvoid setup() {\n    Serial.begin(115200);\n    engine.init();\n    // ... scene setup\n}\n\nvoid loop() {\n    engine.run();\n}\n
    "},{"location":"manual/optimization/platforms_and_drivers/#native-complete-setup","title":"Native Complete Setup","text":"
    #define SDL_MAIN_HANDLED\n#include <SDL2/SDL.h>\n#include <core/Engine.h>\n#include <drivers/native/SDL2_Drawer.h>\n#include <drivers/native/SDL2_AudioBackend.h>\n\nnamespace pr32 = pixelroot32;\n\n// Audio\npr32::drivers::native::SDL2_AudioBackend audioBackend(22050, 1024);\n\n// Display\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::NONE,\n    0, 240, 240\n);\n\n// Input (SDL scancodes)\npr32::input::InputConfig inputConfig(\n    6,\n    SDL_SCANCODE_UP,\n    SDL_SCANCODE_DOWN,\n    SDL_SCANCODE_LEFT,\n    SDL_SCANCODE_RIGHT,\n    SDL_SCANCODE_SPACE,\n    SDL_SCANCODE_RETURN\n);\n\n// Audio config\npr32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n\n// Engine\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\nint main(int argc, char* argv[]) {\n    engine.init();\n    // ... scene setup\n    engine.run();\n    return 0;\n}\n
    "},{"location":"manual/optimization/platforms_and_drivers/#platform-specific-considerations","title":"Platform-Specific Considerations","text":""},{"location":"manual/optimization/platforms_and_drivers/#esp32_1","title":"ESP32","text":"

    Memory: - Limited RAM (~320KB) - Use object pooling - Store data in flash - Avoid dynamic allocation

    Performance: - Target 30-60 FPS - Optimize rendering - Reduce entity count - Profile on hardware

    Hardware: - GPIO pin configuration - SPI display setup - Audio hardware connections - Power considerations

    "},{"location":"manual/optimization/platforms_and_drivers/#native","title":"Native","text":"

    Development: - Fast iteration - Easy debugging - Unlimited resources - Visual debugging tools

    Testing: - Test logic without hardware - Rapid prototyping - CI/CD integration - Cross-platform testing

    "},{"location":"manual/optimization/platforms_and_drivers/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/optimization/platforms_and_drivers/#esp32-display-issues","title":"ESP32 Display Issues","text":"
    • Check wiring connections
    • Verify pin numbers
    • Lower SPI frequency
    • Check display type matches
    • Verify power supply
    "},{"location":"manual/optimization/platforms_and_drivers/#esp32-audio-issues","title":"ESP32 Audio Issues","text":"
    • Check DAC/I2S pin configuration
    • Verify sample rate
    • Check hardware connections
    • Lower volume if distorted
    • Test with different sample rates
    "},{"location":"manual/optimization/platforms_and_drivers/#native-build-issues","title":"Native Build Issues","text":"
    • Verify SDL2 installation
    • Check include/library paths
    • Ensure SDL2 version compatibility
    • Check linker flags
    "},{"location":"manual/optimization/platforms_and_drivers/#next-steps","title":"Next Steps","text":"

    Now that you understand platforms and drivers, learn about: - Extensibility - Create custom drivers - Memory Management - ESP32 memory constraints - Performance Optimization - Platform-specific optimization

    See also: - API Reference - DrawSurface - API Reference - AudioBackend - Getting Started - Your First Project

    "},{"location":"reference/api_overview/","title":"API Reference Overview","text":"

    This document provides a complete technical reference for all PixelRoot32 APIs, organized by module. Each class includes descriptions, constructors, methods, properties, and usage examples.

    "},{"location":"reference/api_overview/#organization","title":"Organization","text":"

    The API is organized into the following modules:

    • Core: Engine, Scene, Entity, Actor, PhysicsActor, SceneManager
    • Graphics: Renderer, Camera2D, Color, Font, Sprite, TileMap, DrawSurface
    • Audio: AudioEngine, MusicPlayer, AudioTypes, AudioConfig, AudioBackend
    • Input: InputManager, InputConfig
    • Physics: CollisionSystem, CollisionTypes
    • UI: UIElement, UIButton, UILabel, UILayouts
    • Particles: ParticleEmitter, ParticleConfig, ParticlePresets
    "},{"location":"reference/api_overview/#quick-navigation","title":"Quick Navigation","text":""},{"location":"reference/api_overview/#core-module","title":"Core Module","text":"
    • Engine - Main engine class, game loop management
    • Scene - Scene/level management
    • Entity - Base game object class
    • Actor - Entity with collision support
    • PhysicsActor - Actor with automatic physics
    • InputManager - Input handling
    • InputConfig - Input configuration
    "},{"location":"reference/api_overview/#graphics-module","title":"Graphics Module","text":"
    • Renderer - High-level rendering API
    • Camera2D - 2D camera for scrolling
    • Color - Color constants and utilities
    • Font - Bitmap font system
    • Sprite - Sprite structures and formats
    • TileMap - Tilemap structure
    • DisplayConfig - Display configuration
    "},{"location":"reference/api_overview/#audio-module","title":"Audio Module","text":"
    • AudioEngine - Sound effects playback
    • MusicPlayer - Background music playback
    • AudioTypes - Audio data structures
    • AudioConfig - Audio configuration
    "},{"location":"reference/api_overview/#physics-module","title":"Physics Module","text":"
    • CollisionSystem - Collision detection
    • CollisionTypes - Collision primitives
    "},{"location":"reference/api_overview/#ui-module","title":"UI Module","text":"
    • UIElement - Base UI element class
    • UIButton - Clickable button
    • UILabel - Text label
    • UILayouts - Layout containers
    "},{"location":"reference/api_overview/#api-documentation-format","title":"API Documentation Format","text":"

    Each API reference page follows this structure:

    "},{"location":"reference/api_overview/#class-name","title":"Class Name","text":"

    Brief description of the class and its purpose.

    "},{"location":"reference/api_overview/#namespace","title":"Namespace","text":"
    namespace pixelroot32::module {\n    class ClassName {\n        // ...\n    };\n}\n
    "},{"location":"reference/api_overview/#constructors","title":"Constructors","text":"

    List of all constructors with parameters.

    "},{"location":"reference/api_overview/#public-methods","title":"Public Methods","text":"Method Description Parameters Returns methodName() Description param: type return type"},{"location":"reference/api_overview/#properties","title":"Properties","text":"Property Type Description property type Description"},{"location":"reference/api_overview/#usage-example","title":"Usage Example","text":"
    // Example code showing typical usage\n
    "},{"location":"reference/api_overview/#performance-notes","title":"Performance Notes","text":"

    Any performance considerations or limitations.

    "},{"location":"reference/api_overview/#see-also","title":"See Also","text":"

    Links to related APIs and documentation.

    "},{"location":"reference/api_overview/#finding-apis","title":"Finding APIs","text":""},{"location":"reference/api_overview/#by-functionality","title":"By Functionality","text":"
    • Game Loop: See Engine
    • Rendering: See Renderer
    • Input: See InputManager
    • Audio: See AudioEngine and MusicPlayer
    • Physics: See PhysicsActor and CollisionSystem
    • UI: See UIElement and layouts
    "},{"location":"reference/api_overview/#by-module","title":"By Module","text":"

    Navigate to the specific module folder: - api_reference/core/ - Core engine classes - api_reference/graphics/ - Rendering and graphics - api_reference/audio/ - Audio system - api_reference/physics/ - Physics and collisions - api_reference/ui/ - User interface

    "},{"location":"reference/api_overview/#complete-api-list","title":"Complete API List","text":""},{"location":"reference/api_overview/#core","title":"Core","text":"
    • Engine
    • Scene
    • Entity
    • Actor
    • PhysicsActor
    • InputManager
    • InputConfig
    "},{"location":"reference/api_overview/#graphics","title":"Graphics","text":"
    • Renderer
    • Camera2D
    • Color
    • Font
    • Sprite
    • TileMap
    • DisplayConfig
    "},{"location":"reference/api_overview/#audio","title":"Audio","text":"
    • AudioEngine
    • MusicPlayer
    • AudioTypes
    • AudioConfig
    "},{"location":"reference/api_overview/#physics","title":"Physics","text":"
    • CollisionSystem
    • CollisionTypes
    "},{"location":"reference/api_overview/#ui","title":"UI","text":"
    • UIElement
    • UIButton
    • UILabel
    • UIVerticalLayout
    • UIHorizontalLayout
    • UIGridLayout
    • UIAnchorLayout
    • UIPanel
    • UIPaddingContainer
    "},{"location":"reference/api_overview/#related-documentation","title":"Related Documentation","text":"
    • Manual - Game Development - How to use the APIs
    • Manual - Advanced Graphics - Advanced techniques
    • Code Examples - Reusable code snippets
    • Game Examples Guide - Learn from complete games

    Note: This is an overview. For detailed API documentation, see the individual reference pages linked above.

    "},{"location":"reference/code_examples/","title":"Code Examples","text":"

    A library of reusable code snippets for common PixelRoot32 tasks. All examples are complete and functional.

    "},{"location":"reference/code_examples/#initialization","title":"Initialization","text":""},{"location":"reference/code_examples/#basic-engine-setup-esp32","title":"Basic Engine Setup (ESP32)","text":"
    #include <Arduino.h>\n#include <core/Engine.h>\n#include <drivers/esp32/TFT_eSPI_Drawer.h>\n#include <drivers/esp32/ESP32_DAC_AudioBackend.h>\n\nnamespace pr32 = pixelroot32;\n\n// Audio\nconst int DAC_PIN = 25;\npr32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(DAC_PIN, 11025);\n\n// Display\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::ST7789,\n    0, 240, 240\n);\n\n// Input\npr32::input::InputConfig inputConfig(6, 32, 27, 33, 14, 13, 12);\n\n// Audio config\npr32::audio::AudioConfig audioConfig(&audioBackend, 11025);\n\n// Engine\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\nvoid setup() {\n    Serial.begin(115200);\n    engine.init();\n    // ... scene setup\n}\n\nvoid loop() {\n    engine.run();\n}\n
    "},{"location":"reference/code_examples/#basic-engine-setup-native","title":"Basic Engine Setup (Native)","text":"
    #define SDL_MAIN_HANDLED\n#include <SDL2/SDL.h>\n#include <core/Engine.h>\n#include <drivers/native/SDL2_Drawer.h>\n#include <drivers/native/SDL2_AudioBackend.h>\n\nnamespace pr32 = pixelroot32;\n\npr32::drivers::native::SDL2_AudioBackend audioBackend(22050, 1024);\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::NONE, 0, 240, 240\n);\npr32::input::InputConfig inputConfig(\n    6, SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, \n    SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT,\n    SDL_SCANCODE_SPACE, SDL_SCANCODE_RETURN\n);\npr32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\nint main(int argc, char* argv[]) {\n    engine.init();\n    // ... scene setup\n    engine.run();\n    return 0;\n}\n
    "},{"location":"reference/code_examples/#entity-movement","title":"Entity Movement","text":""},{"location":"reference/code_examples/#simple-movement","title":"Simple Movement","text":"
    class MovingEntity : public pixelroot32::core::Entity {\nprivate:\n    float speedX = 50.0f;\n    float speedY = 30.0f;\n\npublic:\n    MovingEntity(float x, float y)\n        : Entity(x, y, 16, 16, pixelroot32::core::EntityType::GENERIC) {\n        setRenderLayer(1);\n    }\n\n    void update(unsigned long deltaTime) override {\n        float dt = deltaTime * 0.001f;\n        x += speedX * dt;\n        y += speedY * dt;\n\n        // Bounce off edges\n        if (x < 0 || x > 224) speedX = -speedX;\n        if (y < 0 || y > 224) speedY = -speedY;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x),\n            static_cast<int>(y),\n            width, height,\n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n};\n
    "},{"location":"reference/code_examples/#input-based-movement","title":"Input-Based Movement","text":"
    class PlayerEntity : public pixelroot32::core::Actor {\nprivate:\n    float speed = 100.0f;\n\npublic:\n    PlayerEntity(float x, float y)\n        : Actor(x, y, 16, 16) {\n        setRenderLayer(1);\n    }\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        float dt = deltaTime * 0.001f;\n\n        if (input.isButtonDown(Buttons::LEFT)) {\n            x -= speed * dt;\n        }\n        if (input.isButtonDown(Buttons::RIGHT)) {\n            x += speed * dt;\n        }\n        if (input.isButtonDown(Buttons::UP)) {\n            y -= speed * dt;\n        }\n        if (input.isButtonDown(Buttons::DOWN)) {\n            y += speed * dt;\n        }\n\n        // Keep on screen\n        if (x < 0) x = 0;\n        if (x > 224) x = 224;\n        if (y < 0) y = 0;\n        if (y > 224) y = 224;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x),\n            static_cast<int>(y),\n            width, height,\n            pixelroot32::graphics::Color::White\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Handle collision\n    }\n};\n
    "},{"location":"reference/code_examples/#collisions","title":"Collisions","text":""},{"location":"reference/code_examples/#basic-collision-detection","title":"Basic Collision Detection","text":"
    class CollidableEntity : public pixelroot32::core::Actor {\npublic:\n    CollidableEntity(float x, float y)\n        : Actor(x, y, 16, 16) {\n        setRenderLayer(1);\n        setCollisionLayer(Layers::PLAYER);\n        setCollisionMask(Layers::ENEMY | Layers::WALL);\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        if (other->isInLayer(Layers::ENEMY)) {\n            // Hit enemy\n            takeDamage();\n        } else if (other->isInLayer(Layers::WALL)) {\n            // Hit wall\n            stopMovement();\n        }\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n};\n
    "},{"location":"reference/code_examples/#collision-layers-setup","title":"Collision Layers Setup","text":"
    // Define in GameLayers.h\nnamespace Layers {\n    constexpr uint16_t PLAYER = 0x0001;\n    constexpr uint16_t ENEMY = 0x0002;\n    constexpr uint16_t PROJECTILE = 0x0004;\n    constexpr uint16_t WALL = 0x0008;\n    constexpr uint16_t PICKUP = 0x0010;\n}\n\n// Usage\nplayer->setCollisionLayer(Layers::PLAYER);\nplayer->setCollisionMask(Layers::ENEMY | Layers::WALL);\n\nenemy->setCollisionLayer(Layers::ENEMY);\nenemy->setCollisionMask(Layers::PLAYER | Layers::PROJECTILE);\n
    "},{"location":"reference/code_examples/#sound-effects","title":"Sound Effects","text":""},{"location":"reference/code_examples/#common-sound-effects","title":"Common Sound Effects","text":"
    namespace SoundEffects {\n    inline pixelroot32::audio::AudioEvent jump() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::PULSE;\n        evt.frequency = 600.0f;\n        evt.duration = 0.1f;\n        evt.volume = 0.7f;\n        evt.duty = 0.25f;\n        return evt;\n    }\n\n    inline pixelroot32::audio::AudioEvent coin() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::PULSE;\n        evt.frequency = 1500.0f;\n        evt.duration = 0.12f;\n        evt.volume = 0.8f;\n        evt.duty = 0.5f;\n        return evt;\n    }\n\n    inline pixelroot32::audio::AudioEvent explosion() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::NOISE;\n        evt.frequency = 200.0f;\n        evt.duration = 0.3f;\n        evt.volume = 0.9f;\n        return evt;\n    }\n}\n\n// Usage\nengine.getAudioEngine().playEvent(SoundEffects::jump());\n
    "},{"location":"reference/code_examples/#playing-sound-on-event","title":"Playing Sound on Event","text":"
    class PlayerActor : public pixelroot32::core::Actor {\npublic:\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n\n        if (input.isButtonPressed(Buttons::A)) {\n            // Play jump sound\n            pixelroot32::audio::AudioEvent jumpSound{};\n            jumpSound.type = pixelroot32::audio::WaveType::PULSE;\n            jumpSound.frequency = 800.0f;\n            jumpSound.duration = 0.1f;\n            jumpSound.volume = 0.7f;\n            jumpSound.duty = 0.25f;\n\n            engine.getAudioEngine().playEvent(jumpSound);\n\n            // Jump logic\n            jump();\n        }\n    }\n};\n
    "},{"location":"reference/code_examples/#ui-components","title":"UI Components","text":""},{"location":"reference/code_examples/#simple-menu","title":"Simple Menu","text":"
    class MenuScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIVerticalLayout* menu;\n\npublic:\n    void init() override {\n        menu = new pixelroot32::graphics::ui::UIVerticalLayout(40, 60, 160, 160);\n        menu->setPadding(10);\n        menu->setSpacing(8);\n        menu->setNavigationButtons(0, 1);\n        menu->setButtonStyle(\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Cyan,\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Black\n        );\n\n        menu->addElement(new pixelroot32::graphics::ui::UIButton(\n            \"Start\", 0, 0, 0, 140, 25, []() { startGame(); }\n        ));\n\n        menu->addElement(new pixelroot32::graphics::ui::UIButton(\n            \"Options\", 1, 0, 0, 140, 25, []() { showOptions(); }\n        ));\n\n        addEntity(menu);\n    }\n\n    void update(unsigned long deltaTime) override {\n        menu->handleInput(engine.getInputManager());\n        Scene::update(deltaTime);\n    }\n};\n
    "},{"location":"reference/code_examples/#hud-with-labels","title":"HUD with Labels","text":"
    class GameHUD : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::ui::UILabel* scoreLabel;\n    pixelroot32::graphics::ui::UILabel* livesLabel;\n\npublic:\n    GameHUD()\n        : Entity(0, 0, 240, 240, pixelroot32::core::EntityType::UI_ELEMENT) {\n        setRenderLayer(2);\n\n        scoreLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Score: 0\", 10, 10,\n            pixelroot32::graphics::Color::White, 1\n        );\n\n        livesLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Lives: 3\", 10, 20,\n            pixelroot32::graphics::Color::White, 1\n        );\n    }\n\n    void updateHUD(int score, int lives) {\n        char buffer[32];\n        snprintf(buffer, sizeof(buffer), \"Score: %d\", score);\n        scoreLabel->setText(buffer);\n\n        snprintf(buffer, sizeof(buffer), \"Lives: %d\", lives);\n        livesLabel->setText(buffer);\n    }\n};\n
    "},{"location":"reference/code_examples/#physics","title":"Physics","text":""},{"location":"reference/code_examples/#bouncing-ball","title":"Bouncing Ball","text":"
    class BouncingBall : public pixelroot32::core::PhysicsActor {\npublic:\n    BouncingBall(float x, float y, float radius)\n        : PhysicsActor(x, y, radius * 2, radius * 2) {\n        setRenderLayer(1);\n        setRestitution(0.9f);\n        setFriction(0.05f);\n        setWorldSize(240, 240);\n        setVelocity(50.0f, -30.0f);\n    }\n\n    void update(unsigned long deltaTime) override {\n        float gravity = 200.0f;\n        float dt = deltaTime * 0.001f;\n        setVelocity(vx, vy + gravity * dt);\n        PhysicsActor::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        int radius = width / 2;\n        renderer.drawFilledCircle(\n            static_cast<int>(x + radius),\n            static_cast<int>(y + radius),\n            radius,\n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n};\n
    "},{"location":"reference/code_examples/#platformer-player","title":"Platformer Player","text":"
    class PlatformerPlayer : public pixelroot32::core::PhysicsActor {\nprivate:\n    bool canJump = true;\n    float jumpForce = 250.0f;\n    float moveSpeed = 100.0f;\n\npublic:\n    PlatformerPlayer(float x, float y)\n        : PhysicsActor(x, y, 16, 16) {\n        setRenderLayer(1);\n        setFriction(0.3f);\n        setWorldSize(240, 240);\n    }\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        float dt = deltaTime * 0.001f;\n\n        // Horizontal movement\n        float moveDir = 0.0f;\n        if (input.isButtonDown(Buttons::LEFT)) moveDir -= 1.0f;\n        if (input.isButtonDown(Buttons::RIGHT)) moveDir += 1.0f;\n        setVelocity(moveDir * moveSpeed, vy);\n\n        // Gravity\n        float gravity = 300.0f;\n        setVelocity(vx, vy + gravity * dt);\n\n        // Jump\n        if (input.isButtonPressed(Buttons::A) && canJump) {\n            setVelocity(vx, -jumpForce);\n            canJump = false;\n        }\n\n        PhysicsActor::update(deltaTime);\n\n        // Check if on ground\n        auto collisionInfo = getWorldCollisionInfo();\n        if (collisionInfo.bottom) {\n            canJump = true;\n        }\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n};\n
    "},{"location":"reference/code_examples/#sprites-and-animation","title":"Sprites and Animation","text":""},{"location":"reference/code_examples/#simple-sprite","title":"Simple Sprite","text":"
    // Define sprite data\nstatic const uint16_t PLAYER_SPRITE_DATA[] = {\n    0b00111100,\n    0b01111110,\n    0b11111111,\n    0b11111111,\n    0b11111111,\n    0b01111110,\n    0b00111100,\n    0b00000000\n};\n\nstatic const pixelroot32::graphics::Sprite PLAYER_SPRITE = {\n    PLAYER_SPRITE_DATA, 8, 8\n};\n\n// Draw sprite\nrenderer.drawSprite(PLAYER_SPRITE, 100, 100, \n    pixelroot32::graphics::Color::White);\n
    "},{"location":"reference/code_examples/#sprite-animation","title":"Sprite Animation","text":"
    class AnimatedActor : public pixelroot32::core::Actor {\nprivate:\n    pixelroot32::graphics::SpriteAnimation animation;\n    unsigned long timer = 0;\n    const unsigned long FRAME_DURATION_MS = 100;\n\npublic:\n    AnimatedActor(float x, float y)\n        : Actor(x, y, 8, 8) {\n        setRenderLayer(1);\n        animation.frames = WALK_ANIMATION_FRAMES;\n        animation.frameCount = 3;\n        animation.current = 0;\n    }\n\n    void update(unsigned long deltaTime) override {\n        timer += deltaTime;\n        if (timer >= FRAME_DURATION_MS) {\n            timer -= FRAME_DURATION_MS;\n            animation.step();\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        const auto* frame = animation.frames[animation.current].sprite;\n        renderer.drawSprite(*frame, static_cast<int>(x), static_cast<int>(y),\n            pixelroot32::graphics::Color::White);\n    }\n};\n
    "},{"location":"reference/code_examples/#camera-and-scrolling","title":"Camera and Scrolling","text":""},{"location":"reference/code_examples/#basic-camera-follow","title":"Basic Camera Follow","text":"
    class ScrollingScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    PlayerActor* player;\n\npublic:\n    void init() override {\n        int screenWidth = engine.getRenderer().getWidth();\n        int screenHeight = engine.getRenderer().getHeight();\n\n        camera = pixelroot32::graphics::Camera2D(screenWidth, screenHeight);\n        camera.setBounds(0, 2000 - screenWidth);\n\n        player = new PlayerActor(100, 100);\n        addEntity(player);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n        camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        camera.apply(renderer);\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"reference/code_examples/#tilemaps","title":"Tilemaps","text":""},{"location":"reference/code_examples/#simple-tilemap","title":"Simple Tilemap","text":"
    // Define tiles\nstatic const uint16_t TILE_EMPTY_BITS[] = { /* ... */ };\nstatic const uint16_t TILE_GROUND_BITS[] = { /* ... */ };\n\nstatic const pixelroot32::graphics::Sprite TILES[] = {\n    { TILE_EMPTY_BITS, 8, 8 },\n    { TILE_GROUND_BITS, 8, 8 }\n};\n\n// Create tilemap\nstatic uint8_t TILEMAP_INDICES[30 * 20];\nstatic pixelroot32::graphics::TileMap levelTileMap = {\n    TILEMAP_INDICES, 30, 20, TILES, 8, 8, 2\n};\n\n// Initialize\nvoid initTilemap() {\n    for (int i = 0; i < 30 * 20; i++) {\n        TILEMAP_INDICES[i] = 0;\n    }\n    // Set ground row\n    for (int x = 0; x < 30; x++) {\n        TILEMAP_INDICES[19 * 30 + x] = 1; // Ground tile\n    }\n}\n\n// Draw\nrenderer.drawTileMap(levelTileMap, 0, 0, \n    pixelroot32::graphics::Color::White);\n
    "},{"location":"reference/code_examples/#particles","title":"Particles","text":""},{"location":"reference/code_examples/#explosion-effect","title":"Explosion Effect","text":"
    #include <graphics/particles/ParticleEmitter.h>\n#include <graphics/particles/ParticlePresets.h>\n\nclass ExplosionEffect : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::particles::ParticleEmitter* emitter;\n\npublic:\n    ExplosionEffect()\n        : Entity(0, 0, 1, 1, pixelroot32::core::EntityType::GENERIC) {\n        setRenderLayer(1);\n        emitter = new pixelroot32::graphics::particles::ParticleEmitter(\n            0, 0,\n            pixelroot32::graphics::particles::ParticlePresets::Explosion()\n        );\n    }\n\n    void trigger(float x, float y) {\n        this->x = x;\n        this->y = y;\n        emitter->burst(x, y, 25);\n    }\n\n    void update(unsigned long deltaTime) override {\n        emitter->update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        emitter->draw(renderer);\n    }\n};\n
    "},{"location":"reference/code_examples/#object-pooling","title":"Object Pooling","text":""},{"location":"reference/code_examples/#entity-pool","title":"Entity Pool","text":"
    template<typename T, int POOL_SIZE>\nclass EntityPool {\nprivate:\n    T pool[POOL_SIZE];\n    bool inUse[POOL_SIZE];\n    int activeCount = 0;\n\npublic:\n    EntityPool() {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            inUse[i] = false;\n        }\n    }\n\n    T* acquire() {\n        if (activeCount >= POOL_SIZE) return nullptr;\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (!inUse[i]) {\n                inUse[i] = true;\n                activeCount++;\n                return &pool[i];\n            }\n        }\n        return nullptr;\n    }\n\n    void release(T* obj) {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (&pool[i] == obj) {\n                inUse[i] = false;\n                activeCount--;\n                obj->isEnabled = false;\n                obj->isVisible = false;\n                break;\n            }\n        }\n    }\n};\n
    "},{"location":"reference/code_examples/#common-patterns","title":"Common Patterns","text":""},{"location":"reference/code_examples/#state-machine","title":"State Machine","text":"
    enum class GameState {\n    MENU,\n    PLAYING,\n    PAUSED,\n    GAME_OVER\n};\n\nclass GameScene : public pixelroot32::core::Scene {\nprivate:\n    GameState currentState = GameState::MENU;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        switch (currentState) {\n            case GameState::MENU:\n                updateMenu();\n                break;\n            case GameState::PLAYING:\n                updateGame();\n                break;\n            case GameState::PAUSED:\n                updatePause();\n                break;\n            case GameState::GAME_OVER:\n                updateGameOver();\n                break;\n        }\n        Scene::update(deltaTime);\n    }\n};\n
    "},{"location":"reference/code_examples/#timer-pattern","title":"Timer Pattern","text":"
    class Timer {\nprivate:\n    unsigned long duration;\n    unsigned long elapsed = 0;\n    bool active = false;\n\npublic:\n    Timer(unsigned long ms) : duration(ms) {}\n\n    void start() {\n        active = true;\n        elapsed = 0;\n    }\n\n    void update(unsigned long deltaTime) {\n        if (active) {\n            elapsed += deltaTime;\n            if (elapsed >= duration) {\n                active = false;\n            }\n        }\n    }\n\n    bool isFinished() const { return !active && elapsed >= duration; }\n    bool isActive() const { return active; }\n    float getProgress() const { return static_cast<float>(elapsed) / duration; }\n};\n
    "},{"location":"reference/code_examples/#see-also","title":"See Also","text":"
    • API Reference Overview - Complete API documentation
    • Game Examples Guide - Learn from complete games
    • Manual - Game Development - Detailed guides
    "},{"location":"reference/game_examples_guide/","title":"Game Examples Guide","text":"

    This guide analyzes the complete game examples included with PixelRoot32, explaining their architecture, patterns, and lessons learned.

    "},{"location":"reference/game_examples_guide/#available-examples","title":"Available Examples","text":"

    PixelRoot32 (en el proyecto PixelRoot32 Game Samples) incluye estos juegos y demos:

    • Metroidvania: Plataformas 2D con tilemap 4bpp multicapa y colisi\u00f3n tile-based (requiere PIXELROOT32_ENABLE_4BPP_SPRITES; sin scroll/c\u00e1mara)
    • Space Invaders: Shooter completo con enemigos, proyectiles, b\u00fankeres y audio
    • Pong: Cl\u00e1sico con f\u00edsica y colisiones
    • BrickBreaker: Estilo Breakout con part\u00edculas y audio avanzado
    • Snake: Juego en rejilla con entity pooling
    • TicTacToe: Turnos y IA simple
    • CameraDemo: Plataformas con c\u00e1mara y parallax
    • SpritesDemo: Sprites 2bpp y 4bpp
    • TileMapDemo: Tilemaps 4bpp (con viewport culling)
    "},{"location":"reference/game_examples_guide/#space-invaders","title":"Space Invaders","text":"

    Location: src/examples/SpaceInvaders/

    "},{"location":"reference/game_examples_guide/#architecture","title":"Architecture","text":"

    Space Invaders demonstrates a complete game with multiple systems:

    • Scene Management: SpaceInvadersScene manages game state
    • Actor Hierarchy: PlayerActor, AlienActor, ProjectileActor, BunkerActor
    • Collision System: Uses collision layers for player, enemies, projectiles
    • Audio Integration: Sound effects for shooting, explosions, music
    • Background: Starfield (patr\u00f3n de estrellas en c\u00f3digo) o tilemap
    "},{"location":"reference/game_examples_guide/#key-systems","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#collision-layers","title":"Collision Layers","text":"
    namespace Layers {\n    constexpr uint16_t PLAYER = 0x0001;\n    constexpr uint16_t ALIEN = 0x0002;\n    constexpr uint16_t PROJECTILE = 0x0004;\n    constexpr uint16_t BUNKER = 0x0008;\n}\n\n// Player can collide with aliens and bunkers\nplayer->setCollisionLayer(Layers::PLAYER);\nplayer->setCollisionMask(Layers::ALIEN | Layers::BUNKER);\n\n// Projectiles can hit aliens and bunkers\nprojectile->setCollisionLayer(Layers::PROJECTILE);\nprojectile->setCollisionMask(Layers::ALIEN | Layers::BUNKER);\n
    "},{"location":"reference/game_examples_guide/#entity-management","title":"Entity Management","text":"
    • Uses object pooling for projectiles
    • Manages alien formation with grid layout
    • Handles game state (playing, game over)
    "},{"location":"reference/game_examples_guide/#audio-integration","title":"Audio Integration","text":"
    • Background music using MusicPlayer
    • Sound effects for various events
    • Audio events triggered on collisions
    "},{"location":"reference/game_examples_guide/#patterns-used","title":"Patterns Used","text":"
    • Object Pooling: Projectiles are pooled and reused
    • State Machine: Game states (playing, game over, victory)
    • Grid Layout: Alien formation uses grid-based positioning
    • Event-Driven Audio: Sounds triggered by game events
    "},{"location":"reference/game_examples_guide/#lessons-learned","title":"Lessons Learned","text":"
    • Collision layers are essential for complex games
    • Object pooling improves performance
    • Starfield or tilemap backgrounds are efficient
    • Audio enhances game feel significantly
    "},{"location":"reference/game_examples_guide/#metroidvania","title":"Metroidvania","text":"

    Ubicaci\u00f3n: src/examples/Games/Metroidvania/

    "},{"location":"reference/game_examples_guide/#arquitectura","title":"Arquitectura","text":"

    Metroidvania es un ejemplo de plataformas 2D con tilemap multicapa y optimizaciones pensadas para ESP32. No usa scroll ni c\u00e1mara; el nivel se dibuja con origen fijo (0,0).

    • Scene: MetroidvaniaScene con un \u00fanico PlayerActor y varias capas de tilemap (background, platforms, details, stairs).
    • PlayerActor: Movimiento horizontal y vertical, escaleras, colisi\u00f3n tile-based (sin listas de rect\u00e1ngulos).
    • Tilemap: 4bpp (TileMap4bpp), con viewport culling y cach\u00e9 de paleta en el motor. Nivel 40\u00d730 tiles (320\u00d7240 px).
    • Sin c\u00e1mara: La vista no sigue al jugador; para scroll habr\u00eda que usar Camera2D y aplicar offset en el renderer (como en CameraDemo).
    "},{"location":"reference/game_examples_guide/#caracteristicas-del-motor-usadas","title":"Caracter\u00edsticas del motor usadas","text":"
    • Colisi\u00f3n tile-based: Comprobaci\u00f3n directa de tiles alrededor del jugador (getTileAt), en lugar de iterar sobre platformRects.
    • Sprites 4bpp: Player con animaciones (idle, run, jump) desde headers generados (p. ej. Sprite Compiler).
    • Optimizaciones de renderizado: Viewport culling en drawTileMap, drawSprite 4bpp optimizado, capas culleadas por viewport.
    • Opcional: Scene arena, DMA, IRAM_ATTR en rutas cr\u00edticas (seg\u00fan plan de optimizaci\u00f3n del ejemplo).
    "},{"location":"reference/game_examples_guide/#patrones-utilizados","title":"Patrones utilizados","text":"
    • Tile-based collision: Un solo acceso por tile en O(1) en lugar de O(N) rect\u00e1ngulos.
    • Detecci\u00f3n de escaleras: Un solo resultado reutilizado para colisi\u00f3n y cambio de estado.
    • Hitbox simplificada: Menos puntos de comprobaci\u00f3n vertical (cabeza y pies).
    "},{"location":"reference/game_examples_guide/#lecciones","title":"Lecciones","text":"
    • La colisi\u00f3n tile-based escala mejor que listas de rect\u00e1ngulos en niveles grandes.
    • Las optimizaciones de viewport y 4bpp mejoran FPS en ESP32.
    • Metroidvania sirve de referencia para juegos de plataformas con tilemap y c\u00e1mara.
    "},{"location":"reference/game_examples_guide/#pong","title":"Pong","text":"

    Location: src/examples/Pong/

    "},{"location":"reference/game_examples_guide/#architecture_1","title":"Architecture","text":"

    Pong demonstrates physics and collision handling:

    • PhysicsActor: Ball uses PhysicsActor for automatic physics
    • Collision Callbacks: Paddles and ball handle collisions
    • Score System: Simple score tracking and display
    • Game State: Reset and game over handling
    "},{"location":"reference/game_examples_guide/#key-systems_1","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#physics-setup","title":"Physics Setup","text":"
    class BallActor : public pixelroot32::core::PhysicsActor {\npublic:\n    BallActor(float x, float y, float speed, int radius)\n        : PhysicsActor(x, y, radius * 2, radius * 2) {\n        setRestitution(0.8f);  // Bouncy\n        setFriction(0.1f);     // Low friction\n        setWorldSize(240, 240);\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#collision-response","title":"Collision Response","text":"
    void BallActor::onCollision(pixelroot32::core::Actor* other) {\n    // Adjust ball position\n    // Modify velocity based on impact point\n    // Play bounce sound\n}\n
    "},{"location":"reference/game_examples_guide/#patterns-used_1","title":"Patterns Used","text":"
    • Physics Integration: Uses PhysicsActor for automatic movement
    • Collision Response: Custom collision handling
    • Score Management: Simple state tracking
    • Audio Feedback: Sound on collision
    "},{"location":"reference/game_examples_guide/#lessons-learned_1","title":"Lessons Learned","text":"
    • PhysicsActor simplifies physics-based games
    • Collision callbacks allow custom response logic
    • Simple games can be very effective
    "},{"location":"reference/game_examples_guide/#snake","title":"Snake","text":"

    Location: src/examples/Snake/

    "},{"location":"reference/game_examples_guide/#architecture_2","title":"Architecture","text":"

    Snake demonstrates entity pooling and grid-based movement:

    • Entity Pooling: Snake segments are pooled
    • Grid Movement: Movement constrained to grid
    • Game Logic: Food spawning, collision detection
    • State Management: Game over, reset functionality
    "},{"location":"reference/game_examples_guide/#key-systems_2","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#entity-pooling","title":"Entity Pooling","text":"
    class SnakeScene {\nprivate:\n    std::vector<SnakeSegmentActor*> segmentPool;\n    std::vector<SnakeSegmentActor*> snakeSegments;\n\n    void resetGame() {\n        // Reuse pooled segments\n        for (int i = 0; i < initialLength; ++i) {\n            SnakeSegmentActor* segment = segmentPool[i];\n            segment->resetAlive();\n            snakeSegments.push_back(segment);\n            addEntity(segment);\n        }\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#grid-based-movement","title":"Grid-Based Movement","text":"
    class SnakeSegmentActor : public pixelroot32::core::Actor {\nprivate:\n    int cellX, cellY;  // Grid position\n\npublic:\n    void setCellPosition(int x, int y) {\n        cellX = x;\n        cellY = y;\n        // Convert to world position\n        this->x = cellX * CELL_SIZE;\n        this->y = cellY * CELL_SIZE;\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#patterns-used_2","title":"Patterns Used","text":"
    • Object Pooling: Segments are pre-allocated and reused
    • Grid System: Discrete grid-based movement
    • Linked List: Snake segments form a linked structure
    • Food Spawning: Random food placement with collision checking
    "},{"location":"reference/game_examples_guide/#lessons-learned_2","title":"Lessons Learned","text":"
    • Entity pooling is essential for dynamic entities
    • Grid-based movement simplifies collision detection
    • Pre-allocation avoids memory fragmentation
    "},{"location":"reference/game_examples_guide/#tictactoe","title":"TicTacToe","text":"

    Location: src/examples/TicTacToe/

    "},{"location":"reference/game_examples_guide/#architecture_3","title":"Architecture","text":"

    TicTacToe demonstrates turn-based logic and simple AI:

    • Turn Management: Player vs AI turns
    • Game Board: 3x3 grid representation
    • Win Detection: Check for winning conditions
    • Simple AI: Random move selection
    "},{"location":"reference/game_examples_guide/#key-systems_3","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#board-representation","title":"Board Representation","text":"
    class TicTacToeScene {\nprivate:\n    int board[3][3];  // 0=empty, 1=X, 2=O\n    bool playerTurn = true;\n\n    bool makeMove(int row, int col, int player) {\n        if (board[row][col] == 0) {\n            board[row][col] = player;\n            return true;\n        }\n        return false;\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#win-detection","title":"Win Detection","text":"
    int checkWinner() {\n    // Check rows\n    for (int i = 0; i < 3; i++) {\n        if (board[i][0] == board[i][1] && board[i][1] == board[i][2]) {\n            return board[i][0];\n        }\n    }\n    // Check columns, diagonals...\n    return 0; // No winner\n}\n
    "},{"location":"reference/game_examples_guide/#patterns-used_3","title":"Patterns Used","text":"
    • State Machine: Turn-based state management
    • Grid Logic: 2D array for board representation
    • Simple AI: Random valid move selection
    • UI Integration: Buttons for player input
    "},{"location":"reference/game_examples_guide/#lessons-learned_3","title":"Lessons Learned","text":"
    • Turn-based games are straightforward to implement
    • Simple AI can be effective for basic games
    • Grid-based logic is easy to reason about
    "},{"location":"reference/game_examples_guide/#camerademo","title":"CameraDemo","text":"

    Location: src/examples/CameraDemo/

    "},{"location":"reference/game_examples_guide/#architecture_4","title":"Architecture","text":"

    CameraDemo demonstrates scrolling and parallax:

    • Camera2D: Camera following player
    • Tilemap: Level built with tilemap
    • Parallax: Multiple background layers
    • Platformer Physics: Player with jumping and gravity
    "},{"location":"reference/game_examples_guide/#key-systems_4","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#camera-setup","title":"Camera Setup","text":"
    class CameraDemoScene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    float levelWidth;\n\npublic:\n    void init() override {\n        camera = pixelroot32::graphics::Camera2D(240, 240);\n        camera.setBounds(0, levelWidth - 240);\n    }\n\n    void update(unsigned long deltaTime) override {\n        camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        camera.apply(renderer);\n        renderer.drawTileMap(levelTileMap, 0, 0, Color::White);\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#platformer-physics","title":"Platformer Physics","text":"
    class PlayerCube : public pixelroot32::core::PhysicsActor {\npublic:\n    void update(unsigned long deltaTime) override {\n        // Input handling\n        // Gravity application\n        // Jump logic\n        // Platform collision\n        PhysicsActor::update(deltaTime);\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#patterns-used_4","title":"Patterns Used","text":"
    • Camera Following: Dead-zone camera following
    • Tilemap Rendering: Efficient level rendering
    • Parallax Scrolling: Multiple background layers
    • Platform Collision: Custom collision with platforms
    "},{"location":"reference/game_examples_guide/#lessons-learned_4","title":"Lessons Learned","text":"
    • Camera system enables large levels
    • Tilemaps are efficient for level data
    • Parallax adds depth to 2D games
    • Platform collision requires custom logic
    "},{"location":"reference/game_examples_guide/#spritesdemo","title":"SpritesDemo","text":"

    Location: src/examples/SpritesDemo/

    "},{"location":"reference/game_examples_guide/#architecture_5","title":"Architecture","text":"

    SpritesDemo showcases advanced sprite formats:

    • 2bpp Sprites: 4-color sprite format
    • 4bpp Sprites: 16-color sprite format (if enabled)
    • Animation: Sprite animation examples
    • Format Comparison: Side-by-side format display
    "},{"location":"reference/game_examples_guide/#key-systems_5","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#2bpp-sprite-usage","title":"2bpp Sprite Usage","text":"
    #ifdef PIXELROOT32_ENABLE_2BPP_SPRITES\nstatic const pixelroot32::graphics::Sprite2bpp SPRITE_2BPP = {\n    SPRITE_DATA,\n    SPRITE_PALETTE,\n    16, 32, 4\n};\n\nrenderer.drawSprite(SPRITE_2BPP, x, y, false);\n#endif\n
    "},{"location":"reference/game_examples_guide/#animation-display","title":"Animation Display","text":"
    class SpritesDemoActor : public pixelroot32::core::Entity {\nprivate:\n    unsigned long timer = 0;\n    uint8_t currentFrame = 0;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        timer += deltaTime;\n        if (timer >= 150) {\n            timer -= 150;\n            currentFrame = (currentFrame + 1) % 9;\n        }\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#patterns-used_5","title":"Patterns Used","text":"
    • Format Comparison: Shows different sprite formats
    • Animation Loop: Frame-based animation
    • Conditional Compilation: Uses build flags
    "},{"location":"reference/game_examples_guide/#lessons-learned_5","title":"Lessons Learned","text":"
    • Advanced formats provide more color options
    • Animation is straightforward with frame arrays
    • Build flags enable/disable experimental features
    "},{"location":"reference/game_examples_guide/#common-patterns-across-examples","title":"Common Patterns Across Examples","text":""},{"location":"reference/game_examples_guide/#scene-initialization","title":"Scene Initialization","text":"

    All examples follow this pattern:

    void init() override {\n    // 1. Set palette\n    pixelroot32::graphics::setPalette(PaletteType::NES);\n\n    // 2. Create background entity\n    addEntity(new BackgroundEntity());\n\n    // 3. Create game entities\n    player = new PlayerActor(...);\n    addEntity(player);\n\n    // 4. Initialize game state\n    resetGame();\n}\n
    "},{"location":"reference/game_examples_guide/#update-pattern","title":"Update Pattern","text":"
    void update(unsigned long deltaTime) override {\n    // 1. Process input\n    handleInput();\n\n    // 2. Update game logic\n    updateGameLogic();\n\n    // 3. Call parent update (updates all entities)\n    Scene::update(deltaTime);\n\n    // 4. Post-update logic\n    checkGameState();\n}\n
    "},{"location":"reference/game_examples_guide/#draw-pattern","title":"Draw Pattern","text":"
    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // 1. Apply camera (if used)\n    camera.apply(renderer);\n\n    // 2. Draw background/tilemap\n    renderer.drawTileMap(background, 0, 0, Color::White);\n\n    // 3. Call parent draw (draws all entities)\n    Scene::draw(renderer);\n\n    // 4. Draw UI/HUD\n    drawHUD(renderer);\n}\n
    "},{"location":"reference/game_examples_guide/#learning-path","title":"Learning Path","text":""},{"location":"reference/game_examples_guide/#beginner-examples","title":"Beginner Examples","text":"
    1. Pong: F\u00edsica y colisiones b\u00e1sicas
    2. TicTacToe: L\u00f3gica por turnos
    3. Snake: Entity pooling y rejilla
    "},{"location":"reference/game_examples_guide/#intermediate-examples","title":"Intermediate Examples","text":"
    1. CameraDemo: C\u00e1mara y parallax
    2. SpritesDemo: Formatos 2bpp y 4bpp
    3. BrickBreaker: F\u00edsica, part\u00edculas y audio
    "},{"location":"reference/game_examples_guide/#advanced-examples","title":"Advanced Examples","text":"
    1. Space Invaders: Juego completo (sprites 1bpp, colisiones, audio)
    2. Metroidvania: Plataformas con tilemap 4bpp multicapa, colisi\u00f3n tile-based y optimizaciones ESP32 (sin scroll/c\u00e1mara)
    "},{"location":"reference/game_examples_guide/#code-study-recommendations","title":"Code Study Recommendations","text":""},{"location":"reference/game_examples_guide/#for-learning-physics","title":"For Learning Physics","text":"
    • Study Pong/BallActor.cpp - PhysicsActor usage
    • Study CameraDemo/PlayerCube.cpp - Platformer physics
    "},{"location":"reference/game_examples_guide/#for-learning-collisions","title":"For Learning Collisions","text":"
    • Study SpaceInvaders - Complex collision layers
    • Study Pong - Simple collision response
    "},{"location":"reference/game_examples_guide/#for-learning-memory-management","title":"For Learning Memory Management","text":"
    • Study Snake/SnakeScene.cpp - Entity pooling
    • Study SpaceInvaders - Projectile pooling
    "},{"location":"reference/game_examples_guide/#for-learning-audio","title":"For Learning Audio","text":"
    • Study SpaceInvaders - Music and sound effects
    • Study Pong - Simple audio integration
    "},{"location":"reference/game_examples_guide/#for-learning-ui","title":"For Learning UI","text":"
    • Study TicTacToe - Button-based UI
    • Study menu scenes - Layout usage
    "},{"location":"reference/game_examples_guide/#extending-examples","title":"Extending Examples","text":""},{"location":"reference/game_examples_guide/#adding-features","title":"Adding Features","text":"
    • Pong: Add power-ups, multiple balls
    • Snake: Add obstacles, multiple food types
    • Space Invaders: Add boss battles, power-ups
    "},{"location":"reference/game_examples_guide/#creating-variations","title":"Creating Variations","text":"
    • Pong: Make it vertical, add walls
    • Snake: Change to hexagonal grid
    • TicTacToe: Make it 4x4 or 5x5
    "},{"location":"reference/game_examples_guide/#best-practices-from-examples","title":"Best Practices from Examples","text":"
    1. Pre-allocate Resources: All examples pre-allocate entities
    2. Use Object Pooling: For frequently created/destroyed entities
    3. Organize by Layers: Clear collision layer organization
    4. Separate Concerns: Game logic separate from rendering
    5. State Management: Clear game state handling
    "},{"location":"reference/game_examples_guide/#see-also","title":"See Also","text":"
    • Code Examples - Reusable code snippets
    • API Reference Overview - Complete API documentation
    • Manual - Game Development - Detailed guides

    Note: All example code is available in the src/examples/ directory of the PixelRoot32 Game Samples project.

    "},{"location":"resources/available_tools/","title":"Available Tools","text":"

    This guide documents tools available to facilitate PixelRoot32 game development.

    "},{"location":"resources/available_tools/#sprite-compiler-pr32-sprite-compiler","title":"Sprite Compiler (pr32-sprite-compiler)","text":"

    The Sprite Compiler converts PNG images to PixelRoot32 sprite data formats, making it easy to create sprites from image files.

    Read more in the Sprite Compiler Guide

    From Source:

    git clone https://github.com/Gperez88/pr32-sprite-compiler.git\ncd pr32-sprite-compiler\nnpm install\nnpm link  # Optional: install globally\n

    As NPM Package:

    npm install -g pr32-sprite-compiler\n
    "},{"location":"resources/available_tools/#basic-usage","title":"Basic Usage","text":"

    Command Line:

    pr32-sprite-compiler input.png output.h\n

    With Options:

    pr32-sprite-compiler input.png output.h --format 1bpp --name MY_SPRITE\n
    "},{"location":"resources/available_tools/#supported-formats","title":"Supported Formats","text":"
    • 1bpp (default): Monochrome, most memory-efficient
    • 2bpp: 4 colors per sprite (requires PIXELROOT32_ENABLE_2BPP_SPRITES)
    • 4bpp: 16 colors per sprite (requires PIXELROOT32_ENABLE_4BPP_SPRITES)
    "},{"location":"resources/available_tools/#output-format","title":"Output Format","text":"

    The compiler generates C++ header files with sprite data:

    // output.h\n#ifndef SPRITE_DATA_H\n#define SPRITE_DATA_H\n\n#include <stdint.h>\n\nstatic const uint16_t MY_SPRITE_DATA[] = {\n    0b00111100,\n    0b01111110,\n    0b11111111,\n    // ... more rows\n};\n\nstatic const pixelroot32::graphics::Sprite MY_SPRITE = {\n    MY_SPRITE_DATA,\n    8,  // width\n    8   // height\n};\n\n#endif\n
    "},{"location":"resources/available_tools/#advanced-options","title":"Advanced Options","text":"

    Batch Processing:

    pr32-sprite-compiler --batch sprites/*.png --output-dir sprites/out/\n

    Custom Palette:

    pr32-sprite-compiler input.png output.h --palette custom_palette.json\n

    Sprite Sheet:

    pr32-sprite-compiler sheet.png output.h --sheet 8x8 --count 16\n
    "},{"location":"resources/available_tools/#gui-version","title":"GUI Version","text":"

    If available, a GUI version provides visual feedback:

    • Drag and drop images
    • Preview sprite data
    • Adjust settings visually
    • Export to header files
    "},{"location":"resources/available_tools/#step-by-step-example","title":"Step-by-Step Example","text":"
    1. Create or find a PNG image (8x8, 16x16, etc.)

    2. Run the compiler:

    pr32-sprite-compiler player.png player_sprite.h --name PLAYER_SPRITE\n
    1. Include in your project:
    #include \"player_sprite.h\"\n\nvoid draw() {\n    renderer.drawSprite(PLAYER_SPRITE, 100, 100, Color::White);\n}\n
    "},{"location":"resources/available_tools/#troubleshooting","title":"Troubleshooting","text":"

    Image too large:

    • Sprites must be \u2264 16 pixels wide for 1bpp
    • Reduce image size or split into multiple sprites

    Colors not converting correctly:

    • Ensure image uses indexed colors
    • Use black/white for 1bpp
    • Use 4 colors for 2bpp, 16 for 4bpp

    Output file not found:

    • Check write permissions
    • Verify output path exists
    "},{"location":"resources/available_tools/#future-tools","title":"Future Tools","text":""},{"location":"resources/available_tools/#music-compiler-planned","title":"Music Compiler (Planned)","text":"

    A tool to convert music files or MIDI to PixelRoot32 MusicTrack format.

    Planned Features:

    • MIDI to MusicTrack conversion
    • Visual music editor
    • Instrument preset management
    • Export to C++ header files
    "},{"location":"resources/available_tools/#tilemap-compiler-planned","title":"Tilemap Compiler (Planned)","text":"

    A tool to create tilemaps from image files or tile editors.

    Planned Features:

    • Image to tilemap conversion
    • Tile editor integration
    • Export to C++ arrays
    • Collision data generation
    "},{"location":"resources/available_tools/#other-planned-tools","title":"Other Planned Tools","text":"
    • Save System Generator: Generate save/load code
    • Asset Packer: Bundle assets for distribution
    • Performance Profiler: Analyze game performance
    "},{"location":"resources/available_tools/#using-tools-in-development","title":"Using Tools in Development","text":""},{"location":"resources/available_tools/#workflow-integration","title":"Workflow Integration","text":"

    Typical Workflow:

    1. Create/edit sprites in image editor
    2. Compile sprites to C++ headers
    3. Include headers in project
    4. Use sprites in code

    Automation:

    # Build script example\n#!/bin/bash\npr32-sprite-compiler assets/sprites/*.png --output-dir src/sprites/\n# Continue with build...\n
    "},{"location":"resources/available_tools/#best-practices","title":"Best Practices","text":"
    • Organize assets: Keep source images separate from generated code
    • Version control: Commit generated headers, not source images (or both)
    • Naming conventions: Use consistent naming for sprites
    • Batch processing: Process multiple sprites at once when possible
    "},{"location":"resources/available_tools/#see-also","title":"See Also","text":"
    • Sprite Compiler Documentation - Detailed sprite compiler guide
    • Manual - Sprites and Animation - Using sprites in games
    • Troubleshooting - Common tool issues

    Note: Tool availability may vary. Check the PixelRoot32 repository for the latest tool information.

    "},{"location":"resources/faq/","title":"Frequently Asked Questions","text":"

    Common questions about PixelRoot32, organized by category.

    "},{"location":"resources/faq/#general","title":"General","text":""},{"location":"resources/faq/#what-is-pixelroot32","title":"What is PixelRoot32?","text":"

    PixelRoot32 is a lightweight, modular 2D game engine designed for ESP32 microcontrollers. It provides a complete game development framework with rendering, audio, physics, input, and UI systems, optimized for limited hardware resources.

    "},{"location":"resources/faq/#what-platforms-does-it-support","title":"What platforms does it support?","text":"
    • ESP32: Primary platform (TFT displays, GPIO buttons, DAC/I2S audio)
    • Native/Desktop: Development platform (SDL2, keyboard, SDL2 audio)
    "},{"location":"resources/faq/#what-kind-of-games-can-i-make","title":"What kind of games can I make?","text":"

    PixelRoot32 is ideal for: - Retro/arcade-style games - 2D platformers - Shooters - Puzzle games - Simple RPGs - Educational games

    See Limitations and Considerations for what's not suitable.

    "},{"location":"resources/faq/#is-it-free-to-use","title":"Is it free to use?","text":"

    Yes, PixelRoot32 is open source and licensed under the MIT License. You can use it freely for personal and commercial projects.

    "},{"location":"resources/faq/#where-can-i-find-the-source-code","title":"Where can I find the source code?","text":"
    • Engine: https://github.com/Gperez88/PixelRoot32-Game-Engine
    • Samples: https://github.com/Gperez88/PixelRoot32-Game-Samples
    • Documentation: https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Docs
    "},{"location":"resources/faq/#installation-and-configuration","title":"Installation and Configuration","text":""},{"location":"resources/faq/#how-do-i-install-pixelroot32","title":"How do I install PixelRoot32?","text":"

    See Your First Project for detailed installation instructions.

    Quick answer: 1. Install PlatformIO in VS Code 2. Create new ESP32 project 3. Add library dependency: gperez88/PixelRoot32-Game-Engine@0.2.0-dev 4. Configure hardware in platformio.ini

    "},{"location":"resources/faq/#what-version-should-i-use","title":"What version should I use?","text":"

    Use the exact version 0.2.0-dev. Do NOT use ^ or fuzzy versioning, as the API may change.

    "},{"location":"resources/faq/#how-do-i-configure-my-display","title":"How do I configure my display?","text":"

    Configure TFT_eSPI via build flags in platformio.ini. See Your First Project for examples.

    "},{"location":"resources/faq/#how-do-i-set-up-audio","title":"How do I set up audio?","text":"

    Choose an audio backend: - ESP32_DAC: Simple, one pin (GPIO 25 or 26) - ESP32_I2S: Higher quality, requires external DAC - SDL2_AudioBackend: For Native/PC development

    See Audio for details.

    "},{"location":"resources/faq/#development","title":"Development","text":""},{"location":"resources/faq/#how-do-i-create-a-scene","title":"How do I create a scene?","text":"

    Inherit from pixelroot32::core::Scene and implement init(), update(), and draw(). See Scenes and Entities.

    "},{"location":"resources/faq/#how-do-i-add-entities-to-a-scene","title":"How do I add entities to a scene?","text":"

    Create entities and call addEntity() in init(). The scene manages them automatically.

    "},{"location":"resources/faq/#whats-the-difference-between-entity-actor-and-physicsactor","title":"What's the difference between Entity, Actor, and PhysicsActor?","text":"
    • Entity: Base class, can be drawn and updated
    • Actor: Entity with collision detection
    • PhysicsActor: Actor with automatic physics (velocity, gravity, friction)

    See Scenes and Entities for details.

    "},{"location":"resources/faq/#how-do-i-handle-input","title":"How do I handle input?","text":"

    Access InputManager through the engine:

    auto& input = engine.getInputManager();\nif (input.isButtonPressed(Buttons::A)) {\n    // Handle input\n}\n

    See Input and Control.

    "},{"location":"resources/faq/#how-do-i-play-sounds","title":"How do I play sounds?","text":"

    Create an AudioEvent and play it:

    pixelroot32::audio::AudioEvent sound{};\nsound.type = pixelroot32::audio::WaveType::PULSE;\nsound.frequency = 800.0f;\nsound.duration = 0.1f;\nengine.getAudioEngine().playEvent(sound);\n

    See Audio.

    "},{"location":"resources/faq/#how-do-i-create-sprites","title":"How do I create sprites?","text":"

    Define sprite data manually or use the Sprite Compiler tool. See Sprites and Animation.

    "},{"location":"resources/faq/#can-i-use-images-instead-of-manual-sprite-data","title":"Can I use images instead of manual sprite data?","text":"

    Yes, use the Sprite Compiler tool to convert PNG images to sprite data. See Available Tools.

    "},{"location":"resources/faq/#performance","title":"Performance","text":""},{"location":"resources/faq/#why-is-my-game-running-slowly","title":"Why is my game running slowly?","text":"

    Common causes: - Too many entities (MAX_ENTITIES = 32) - Too many draw calls - Expensive calculations in update() - Memory issues

    See Performance Tuning for solutions.

    "},{"location":"resources/faq/#what-fps-should-i-target","title":"What FPS should I target?","text":"

    30-60 FPS is typical. Lower complexity games can achieve 60 FPS, more complex games may need to target 30 FPS.

    "},{"location":"resources/faq/#how-do-i-optimize-my-game","title":"How do I optimize my game?","text":"
    • Use object pooling
    • Implement viewport culling
    • Reduce entity count
    • Cache calculations
    • Use tilemaps for backgrounds

    See Performance Tuning.

    "},{"location":"resources/faq/#memory","title":"Memory","text":""},{"location":"resources/faq/#why-do-i-get-out-of-memory-errors","title":"Why do I get \"out of memory\" errors?","text":"

    ESP32 has limited RAM (~320KB). Solutions: - Use object pooling - Store data in flash (const/constexpr) - Reduce entity count - Avoid dynamic allocation

    See Memory Management.

    "},{"location":"resources/faq/#what-is-max_entities","title":"What is MAX_ENTITIES?","text":"

    MAX_ENTITIES = 32 is a hard limit per scene. This includes all entities: actors, UI elements, particles, etc.

    Solutions: - Use object pooling to reuse entities - Disable entities instead of removing - Combine multiple entities into one

    "},{"location":"resources/faq/#how-do-i-check-available-memory","title":"How do I check available memory?","text":"
    #ifdef PLATFORM_ESP32\nSerial.print(\"Free heap: \");\nSerial.println(ESP.getFreeHeap());\n#endif\n
    "},{"location":"resources/faq/#hardware","title":"Hardware","text":""},{"location":"resources/faq/#which-esp32-board-should-i-use","title":"Which ESP32 board should I use?","text":"

    Any ESP32 board works. Common choices: - ESP32-WROOM-32 - ESP32-WROVER (more RAM) - ESP32-DevKit

    "},{"location":"resources/faq/#which-display-should-i-use","title":"Which display should I use?","text":"

    Popular choices: - ST7789: 240x240, good quality - ST7735: 128x128, smaller/cheaper - ILI9341: 240x320, larger

    See Platforms and Drivers.

    "},{"location":"resources/faq/#how-many-buttons-do-i-need","title":"How many buttons do I need?","text":"

    Minimum: 4 (UP, DOWN, LEFT, RIGHT) Recommended: 6 (add A and B buttons) More buttons can be added if needed.

    "},{"location":"resources/faq/#can-i-use-analog-joysticks","title":"Can I use analog joysticks?","text":"

    Not directly supported. You can read analog pins manually and convert to digital input, but the engine expects digital buttons.

    "},{"location":"resources/faq/#troubleshooting","title":"Troubleshooting","text":""},{"location":"resources/faq/#my-display-is-blank-whats-wrong","title":"My display is blank. What's wrong?","text":"
    1. Check wiring connections
    2. Verify pin numbers in platformio.ini
    3. Lower SPI frequency
    4. Check display type matches hardware
    5. Verify power supply

    See Troubleshooting.

    "},{"location":"resources/faq/#buttons-dont-work-why","title":"Buttons don't work. Why?","text":"
    1. Check button wiring
    2. Verify pin numbers in InputConfig
    3. Check pull-up/pull-down resistors
    4. Ensure InputManager is being updated
    5. Test with isButtonDown() vs isButtonPressed()
    "},{"location":"resources/faq/#audio-is-distorted-how-do-i-fix-it","title":"Audio is distorted. How do I fix it?","text":"
    1. Lower volume levels
    2. Reduce sample rate (try 11025 Hz)
    3. Check for too many simultaneous sounds
    4. Verify hardware connections
    5. Check power supply
    "},{"location":"resources/faq/#game-crashes-randomly-whats-happening","title":"Game crashes randomly. What's happening?","text":"

    Common causes: - Out of memory - Too many entities - Infinite loops - Stack overflow - Watchdog timeout

    See Troubleshooting for debugging techniques.

    "},{"location":"resources/faq/#advanced","title":"Advanced","text":""},{"location":"resources/faq/#can-i-extend-the-engine","title":"Can I extend the engine?","text":"

    Yes, PixelRoot32 is designed to be extensible. You can: - Create custom display drivers - Create custom audio backends - Extend existing systems

    See Extensibility.

    "},{"location":"resources/faq/#can-i-use-3d-graphics","title":"Can I use 3D graphics?","text":"

    No, PixelRoot32 is 2D-only. It's designed for sprite-based 2D games.

    "},{"location":"resources/faq/#can-i-add-networking","title":"Can I add networking?","text":"

    Not currently supported. The engine focuses on single-player games.

    "},{"location":"resources/faq/#can-i-save-game-data","title":"Can I save game data?","text":"

    Not currently supported. A save system is planned for future versions.

    "},{"location":"resources/faq/#can-i-use-multiple-scenes-at-once","title":"Can I use multiple scenes at once?","text":"

    Yes, use SceneManager to push/pop scenes. This is useful for menus and pause screens.

    "},{"location":"resources/faq/#getting-help","title":"Getting Help","text":""},{"location":"resources/faq/#where-can-i-get-help","title":"Where can I get help?","text":"
    • Documentation: This documentation site
    • Examples: Study example games in the samples project
    • Discord: Community Discord server
    • GitHub: Open an issue for bugs
    "},{"location":"resources/faq/#how-do-i-report-a-bug","title":"How do I report a bug?","text":"

    Create a detailed bug report with: - Platform (ESP32 or Native) - Hardware details - Minimal reproduction code - Error messages - Expected vs actual behavior

    "},{"location":"resources/faq/#can-i-contribute","title":"Can I contribute?","text":"

    Yes! PixelRoot32 is open source. Check the main repository for contribution guidelines.

    "},{"location":"resources/faq/#see-also","title":"See Also","text":"
    • Troubleshooting - Detailed problem solving
    • Limitations and Considerations - What the engine can/can't do
    • Getting Started - Start here if you're new
    • Manual - Complete development guides

    Can't find your question? Check the Troubleshooting guide or ask on the Discord server.

    "},{"location":"resources/limitations_and_considerations/","title":"Limitations and Considerations","text":"

    This document honestly documents what PixelRoot32 can and cannot do, helping you make informed decisions about using the engine.

    "},{"location":"resources/limitations_and_considerations/#hardware-limitations-esp32","title":"Hardware Limitations (ESP32)","text":""},{"location":"resources/limitations_and_considerations/#memory-constraints","title":"Memory Constraints","text":"

    RAM: - Available: ~320KB total (varies by ESP32 model) - Heap: Limited and fragmented over time - Stack: ~8KB, avoid large stack allocations - Impact: Limits entity count, sprite data, and dynamic allocation

    Flash: - Available: 4MB+ (varies by model) - Usage: Program code, sprite data, assets - Impact: Large games may approach limits

    Recommendations: - Use object pooling - Store data in flash (const/constexpr) - Avoid dynamic allocation in game loop - Keep entity count low

    "},{"location":"resources/limitations_and_considerations/#cpu-limitations","title":"CPU Limitations","text":"

    Performance: - Clock Speed: 240MHz (typically) - Single-threaded: One core handles everything - Target FPS: 30-60 FPS (depends on complexity) - Frame Budget: ~16-33ms per frame at 60 FPS

    Impact: - Complex games may struggle - Many entities reduce performance - Expensive calculations hurt FPS

    Recommendations: - Optimize rendering - Reduce entity count - Cache calculations - Profile on hardware

    "},{"location":"resources/limitations_and_considerations/#display-limitations","title":"Display Limitations","text":"

    Supported Displays: - TFT displays via SPI (ST7735, ST7789, ILI9341, etc.) - Limited to SPI displays - Resolution typically 128x128 to 320x240

    Constraints: - SPI communication speed limits - Display initialization complexity - Power consumption

    "},{"location":"resources/limitations_and_considerations/#audio-limitations","title":"Audio Limitations","text":"

    Hardware: - Internal DAC: Lower quality, simple setup - I2S: Higher quality, requires external DAC - Sample rates: 11025 Hz (DAC) or 22050 Hz (I2S)

    Constraints: - 4 channels total (2 Pulse, 1 Triangle, 1 Noise) - Music uses one channel - Limited simultaneous sounds - Quality limited by hardware

    "},{"location":"resources/limitations_and_considerations/#software-limitations","title":"Software Limitations","text":""},{"location":"resources/limitations_and_considerations/#entity-system","title":"Entity System","text":"

    MAX_ENTITIES = 32 per scene - Hard limit, cannot be changed easily - Applies to all entities (actors, UI, particles, etc.) - Must manage entity count carefully

    Workarounds: - Use object pooling - Reuse entities - Disable entities instead of removing - Combine multiple entities into one

    "},{"location":"resources/limitations_and_considerations/#no-rtti-runtime-type-information","title":"No RTTI (Runtime Type Information)","text":"

    Impact: - Cannot use dynamic_cast in most code - Type checking must be done manually - Limits polymorphism patterns

    Alternatives: - Use virtual functions - Manual type checking - Tag-based systems

    "},{"location":"resources/limitations_and_considerations/#no-exceptions-in-critical-code","title":"No Exceptions in Critical Code","text":"

    Impact: - Cannot use try/catch in game loop - Error handling must be explicit - Crashes instead of exceptions

    Best Practices: - Validate inputs - Check return values - Use assertions for debugging - Handle errors explicitly

    "},{"location":"resources/limitations_and_considerations/#no-dynamic-allocation-in-game-loop","title":"No Dynamic Allocation in Game Loop","text":"

    Impact: - Cannot use new/delete during gameplay - Must pre-allocate resources - Limits flexibility

    Solutions: - Object pooling - Pre-allocation in init() - Static buffers - Fixed-size arrays

    "},{"location":"resources/limitations_and_considerations/#no-advanced-features","title":"No Advanced Features","text":"

    Not Supported: - 3D graphics - Shaders - Advanced physics (joints, constraints) - Networking - File system (ESP32) - Advanced audio effects

    Focus: - 2D sprite-based games - Simple physics - Retro-style games - Embedded-friendly features

    "},{"location":"resources/limitations_and_considerations/#experimental-features","title":"Experimental Features","text":""},{"location":"resources/limitations_and_considerations/#2bpp-sprites","title":"2bpp Sprites","text":"

    Status: Experimental - Requires PIXELROOT32_ENABLE_2BPP_SPRITES flag - May have bugs or limitations - Not fully tested

    Use with caution: - Test thoroughly - May change in future versions - Report issues if found

    "},{"location":"resources/limitations_and_considerations/#4bpp-sprites","title":"4bpp Sprites","text":"

    Status: Experimental - Requires PIXELROOT32_ENABLE_4BPP_SPRITES flag - More experimental than 2bpp - Higher memory usage

    Use with caution: - Test extensively - Monitor memory usage - May be unstable

    "},{"location":"resources/limitations_and_considerations/#scene-arena","title":"Scene Arena","text":"

    Status: Experimental - Requires PIXELROOT32_ENABLE_SCENE_ARENA flag - Alternative memory management - May have bugs

    Recommendations: - Use object pooling instead (more stable) - Test thoroughly if using - May be removed or changed

    "},{"location":"resources/limitations_and_considerations/#unsupported-features-current","title":"Unsupported Features (Current)","text":""},{"location":"resources/limitations_and_considerations/#planned-but-not-available","title":"Planned but Not Available","text":"
    • u8g2 Driver: Alternative display driver (planned)
    • Music Compiler: Tool to convert music files (planned)
    • Tilemap Compiler: Tool to create tilemaps (planned)
    • Save System: Persistent storage system (planned)
    • Spatial Partitioning: Advanced collision optimization (planned)
    "},{"location":"resources/limitations_and_considerations/#not-planned","title":"Not Planned","text":"
    • 3D Graphics: 2D-only engine
    • Networking: No network support
    • File System: No file I/O on ESP32
    • Advanced Audio: NES-like audio only
    • Scripting: No Lua/JavaScript support
    "},{"location":"resources/limitations_and_considerations/#best-practices-for-esp32","title":"Best Practices for ESP32","text":""},{"location":"resources/limitations_and_considerations/#memory-management","title":"Memory Management","text":"
    • Pre-allocate: All resources in init()
    • Object pooling: Reuse entities
    • Flash storage: Use const/constexpr for data
    • Avoid strings: Use static buffers
    • Monitor usage: Check heap regularly
    "},{"location":"resources/limitations_and_considerations/#performance","title":"Performance","text":"
    • Limit entities: Stay well below MAX_ENTITIES
    • Optimize rendering: Use culling, batching
    • Cache calculations: Avoid repeated work
    • Profile on hardware: PC performance \u2260 ESP32
    "},{"location":"resources/limitations_and_considerations/#development","title":"Development","text":"
    • Test on hardware: Don't rely only on Native
    • Start simple: Add complexity gradually
    • Monitor memory: Watch for leaks
    • Optimize incrementally: Profile and optimize
    "},{"location":"resources/limitations_and_considerations/#what-pixelroot32-is-good-for","title":"What PixelRoot32 IS Good For","text":"

    \u2705 Retro-style 2D games \u2705 Arcade games \u2705 Puzzle games \u2705 Platformers \u2705 Shooters \u2705 Educational projects \u2705 Prototyping \u2705 Embedded game development

    "},{"location":"resources/limitations_and_considerations/#what-pixelroot32-is-not-good-for","title":"What PixelRoot32 is NOT Good For","text":"

    \u274c 3D games \u274c Complex physics simulations \u274c Large open worlds \u274c Games requiring many entities \u274c Games with complex graphics \u274c Network multiplayer \u274c Games requiring file I/O

    "},{"location":"resources/limitations_and_considerations/#making-informed-decisions","title":"Making Informed Decisions","text":""},{"location":"resources/limitations_and_considerations/#before-starting-a-project","title":"Before Starting a Project","text":"
    1. Assess requirements: Does PixelRoot32 fit?
    2. Check limitations: Can you work within constraints?
    3. Plan architecture: Design around limitations
    4. Test early: Verify on hardware early
    "},{"location":"resources/limitations_and_considerations/#if-limitations-are-a-problem","title":"If Limitations Are a Problem","text":"

    Consider alternatives: - Full game engines (Unity, Godot) for complex games - Custom solutions for specific needs - Different hardware for more resources

    Or work within limits: - Simplify game design - Optimize aggressively - Use creative solutions

    "},{"location":"resources/limitations_and_considerations/#version-compatibility","title":"Version Compatibility","text":""},{"location":"resources/limitations_and_considerations/#current-version","title":"Current Version","text":"
    • Engine Version: 0.2.0-dev
    • API Stability: May change
    • Breaking Changes: Possible in future versions

    Recommendations: - Pin exact version in platformio.ini - Don't use ^ or fuzzy versioning - Test after engine updates - Review changelog

    "},{"location":"resources/limitations_and_considerations/#honest-assessment","title":"Honest Assessment","text":"

    PixelRoot32 is designed for: - Simple to medium complexity games - Retro/arcade style - ESP32 hardware constraints - Rapid development

    It is not designed for: - AAA game complexity - Modern graphics - Large-scale games - Unlimited resources

    "},{"location":"resources/limitations_and_considerations/#see-also","title":"See Also","text":"
    • Memory Management - Working with memory limits
    • Performance Tuning - Optimizing performance
    • Troubleshooting - Solving problems
    • FAQ - Common questions

    Remember: Understanding limitations helps you build better games within PixelRoot32's capabilities.

    "},{"location":"resources/troubleshooting/","title":"Troubleshooting","text":"

    This guide helps you diagnose and fix common issues when developing with PixelRoot32.

    "},{"location":"resources/troubleshooting/#compilation-problems","title":"Compilation Problems","text":""},{"location":"resources/troubleshooting/#common-compilation-errors","title":"Common Compilation Errors","text":"

    Error: Library not found

    Solution: Ensure PixelRoot32-Game-Engine is properly installed\n- Check platformio.ini has correct lib_deps\n- Verify library version matches (use exact version, not ^)\n- Try: pio lib install\n

    Error: Include file not found

    Solution: Check include paths\n- Verify lib_extra_dirs in platformio.ini\n- Check that library is in lib/ directory\n- Ensure correct namespace (pixelroot32::)\n

    Error: Undefined reference

    Solution: Link missing libraries\n- Check all required libraries are listed\n- Verify TFT_eSPI is installed for ESP32\n- Check SDL2 is installed for Native builds\n

    Error: Build flags not recognized

    Solution: Verify build flag syntax\n- Use -D FLAG_NAME (not --define)\n- Check flag names are correct\n- Ensure flags are in correct environment section\n

    "},{"location":"resources/troubleshooting/#configuration-issues","title":"Configuration Issues","text":"

    Wrong display type: - Verify DisplayType matches your hardware - Check TFT_eSPI build flags match display - Test with different display types

    Incorrect pin configuration: - Verify GPIO pins match your wiring - Check pin numbers in platformio.ini - Ensure pins aren't used by other peripherals

    "},{"location":"resources/troubleshooting/#hardware-problems-esp32","title":"Hardware Problems (ESP32)","text":""},{"location":"resources/troubleshooting/#display-not-working","title":"Display Not Working","text":"

    Symptoms: - Blank screen - Garbled display - No output

    Solutions: 1. Check wiring: - Verify SPI connections (MOSI, SCLK, DC, RST) - Check power supply (3.3V or 5V as required) - Ensure ground connections

    1. Verify configuration:
    2. Check display type matches hardware
    3. Verify pin numbers in platformio.ini
    4. Test with known working configuration

    5. SPI frequency:

    6. Lower SPI frequency (try 20MHz instead of 40MHz)
    7. Some displays need slower speeds
    8. Check display datasheet for max frequency

    9. Display initialization:

    10. Try different rotation values
    11. Check display width/height settings
    12. Verify TFT_eSPI driver is correct
    "},{"location":"resources/troubleshooting/#buttons-not-responding","title":"Buttons Not Responding","text":"

    Symptoms: - No input detected - Buttons don't trigger actions - Input feels laggy

    Solutions: 1. Check wiring: - Verify button connections to GPIO pins - Check pull-up/pull-down resistors - Test buttons with multimeter

    1. Verify pin configuration:
    2. Check InputConfig pin numbers
    3. Ensure pins match hardware
    4. Verify pins aren't used elsewhere

    5. Input debouncing:

    6. Add hardware debouncing (capacitor)
    7. Check InputManager is being updated
    8. Verify input is read in update(), not draw()

    9. Button logic:

    10. Test with isButtonDown() vs isButtonPressed()
    11. Check button indices match configuration
    12. Verify input is accessed correctly
    "},{"location":"resources/troubleshooting/#audio-not-working","title":"Audio Not Working","text":"

    Symptoms: - No sound output - Distorted audio - Audio glitches

    Solutions: 1. DAC Configuration: - Verify DAC pin (25 or 26 for ESP32) - Check sample rate (11025 Hz recommended) - Ensure audio backend is initialized

    1. I2S Configuration:
    2. Verify I2S pin connections (BCLK, LRCK, DOUT)
    3. Check external DAC is powered
    4. Verify I2S DAC is compatible

    5. Audio quality:

    6. Lower sample rate if distorted
    7. Reduce volume levels
    8. Check for too many simultaneous sounds
    9. Verify audio buffer size

    10. Hardware:

    11. Check speaker connections
    12. Verify amplifier is powered
    13. Test with different audio hardware
    14. Check audio cable connections
    "},{"location":"resources/troubleshooting/#power-issues","title":"Power Issues","text":"

    Symptoms: - ESP32 resets randomly - Display flickers - Unstable operation

    Solutions: 1. Power supply: - Use adequate power supply (500mA+ recommended) - Check voltage is stable (3.3V) - Add decoupling capacitors

    1. Current draw:
    2. Display draws significant current
    3. Audio amplifier adds load
    4. Reduce brightness if possible

    5. Wiring:

    6. Use thick wires for power
    7. Keep power wires short
    8. Add capacitors near ESP32
    "},{"location":"resources/troubleshooting/#performance-problems","title":"Performance Problems","text":""},{"location":"resources/troubleshooting/#low-fps","title":"Low FPS","text":"

    Symptoms: - Game runs slowly - Laggy movement - Stuttering

    Solutions: 1. Reduce entity count: - Limit active entities (MAX_ENTITIES = 32) - Disable off-screen entities - Use object pooling

    1. Optimize rendering:
    2. Use viewport culling
    3. Reduce draw calls
    4. Use tilemaps instead of individual sprites
    5. Limit sprite count

    6. Simplify logic:

    7. Cache expensive calculations
    8. Reduce collision checks
    9. Lower update frequency for non-critical entities

    10. Check hardware:

    11. Verify ESP32 is running at full speed (240MHz)
    12. Check for thermal throttling
    13. Ensure adequate power supply
    "},{"location":"resources/troubleshooting/#frame-drops","title":"Frame Drops","text":"

    Symptoms: - Occasional stuttering - Inconsistent frame times - Periodic freezes

    Solutions: 1. Identify bottlenecks: - Profile frame time - Check for expensive operations - Look for blocking code

    1. Optimize update loop:
    2. Avoid dynamic allocation
    3. Cache calculations
    4. Reduce string operations

    5. Memory issues:

    6. Check for memory leaks
    7. Reduce memory usage
    8. Use object pooling
    "},{"location":"resources/troubleshooting/#freezescrashes","title":"Freezes/Crashes","text":"

    Symptoms: - Game stops responding - ESP32 resets - Watchdog resets

    Solutions: 1. Memory issues: - Check available heap memory - Reduce entity count - Avoid dynamic allocation - Use object pooling

    1. Infinite loops:
    2. Check for infinite loops in update()
    3. Verify collision detection doesn't loop
    4. Check animation logic

    5. Stack overflow:

    6. Avoid large stack allocations
    7. Reduce recursion depth
    8. Move large data to heap (carefully)

    9. Watchdog:

    10. Ensure update() completes quickly
    11. Add yield() calls if needed
    12. Check for blocking operations
    "},{"location":"resources/troubleshooting/#memory-problems","title":"Memory Problems","text":""},{"location":"resources/troubleshooting/#out-of-memory","title":"Out of Memory","text":"

    Symptoms: - Compilation fails - Runtime crashes - \"Allocation failed\" errors

    Solutions: 1. Reduce memory usage: - Use 1bpp sprites instead of 2bpp/4bpp - Store data in flash (const/constexpr) - Reduce entity count - Use object pooling

    1. Optimize data:
    2. Reuse sprites
    3. Compress tilemap data
    4. Remove unused code/data

    5. Memory management:

    6. Avoid dynamic allocation in game loop
    7. Pre-allocate all resources
    8. Use static buffers
    "},{"location":"resources/troubleshooting/#memory-fragmentation","title":"Memory Fragmentation","text":"

    Symptoms: - Gradual performance degradation - Allocation failures over time - Crashes after running for a while

    Solutions: 1. Use object pooling: - Pre-allocate entities - Reuse objects instead of creating/destroying - Avoid frequent new/delete

    1. Pre-allocate resources:
    2. Allocate everything in init()
    3. Use fixed-size arrays
    4. Avoid dynamic containers
    "},{"location":"resources/troubleshooting/#native-build-problems","title":"Native Build Problems","text":""},{"location":"resources/troubleshooting/#sdl2-not-found","title":"SDL2 Not Found","text":"

    Symptoms: - Compilation fails - Linker errors - Missing SDL2 symbols

    Solutions: 1. Install SDL2: - Windows (MSYS2): pacman -S mingw-w64-x86_64-SDL2 - Linux: sudo apt-get install libsdl2-dev - macOS: brew install sdl2

    1. Check paths:
    2. Verify include paths in platformio.ini
    3. Check library paths
    4. Ensure SDL2 version is compatible

    5. Linker flags:

    6. Verify -lSDL2 is in build flags
    7. Check library search paths
    8. Ensure SDL2 DLL is accessible (Windows)
    "},{"location":"resources/troubleshooting/#window-not-opening","title":"Window Not Opening","text":"

    Symptoms: - Program runs but no window - Console shows errors - Immediate exit

    Solutions: 1. Check SDL2 initialization: - Verify SDL2 is properly initialized - Check for SDL2 error messages - Ensure display config is correct

    1. Graphics drivers:
    2. Update graphics drivers
    3. Check SDL2 video backend
    4. Test with simple SDL2 program

    5. Console output:

    6. Run from terminal to see errors
    7. Check for error messages
    8. Verify SDL2 is working
    "},{"location":"resources/troubleshooting/#debugging-techniques","title":"Debugging Techniques","text":""},{"location":"resources/troubleshooting/#serial-debugging-esp32","title":"Serial Debugging (ESP32)","text":"
    void setup() {\n    Serial.begin(115200);\n    // ... initialization\n\n    Serial.println(\"Engine initialized\");\n}\n\nvoid update(unsigned long deltaTime) override {\n    // Debug output\n    if (frameCount % 60 == 0) {\n        Serial.print(\"FPS: \");\n        Serial.println(1000.0f / deltaTime);\n    }\n    frameCount++;\n}\n
    "},{"location":"resources/troubleshooting/#memory-monitoring","title":"Memory Monitoring","text":"
    #ifdef PLATFORM_ESP32\n#include <Arduino.h>\n\nvoid checkMemory() {\n    Serial.print(\"Free heap: \");\n    Serial.println(ESP.getFreeHeap());\n    Serial.print(\"Largest block: \");\n    Serial.println(ESP.getMaxAllocHeap());\n}\n#endif\n
    "},{"location":"resources/troubleshooting/#performance-profiling","title":"Performance Profiling","text":"
    class Profiler {\nprivate:\n    unsigned long updateTime = 0;\n    unsigned long drawTime = 0;\n\npublic:\n    void startUpdate() {\n        updateTime = micros();\n    }\n\n    void endUpdate() {\n        updateTime = micros() - updateTime;\n    }\n\n    void log() {\n        Serial.print(\"Update: \");\n        Serial.print(updateTime);\n        Serial.print(\"us, Draw: \");\n        Serial.println(drawTime);\n    }\n};\n
    "},{"location":"resources/troubleshooting/#visual-debugging","title":"Visual Debugging","text":"
    • Draw hitboxes: Visualize collision boxes
    • Show FPS: Display frame rate on screen
    • Entity count: Show active entity count
    • Memory usage: Display memory statistics
    "},{"location":"resources/troubleshooting/#common-patterns-for-debugging","title":"Common Patterns for Debugging","text":""},{"location":"resources/troubleshooting/#isolate-the-problem","title":"Isolate the Problem","text":"
    1. Minimal reproduction: Create smallest code that shows issue
    2. Disable features: Turn off systems one by one
    3. Test incrementally: Add features back one at a time
    "},{"location":"resources/troubleshooting/#check-the-basics","title":"Check the Basics","text":"
    1. Verify initialization: Ensure engine.init() is called
    2. Check scene setup: Verify scene is set and initialized
    3. Test on both platforms: Compare ESP32 vs Native behavior
    4. Review recent changes: What changed before the issue?
    "},{"location":"resources/troubleshooting/#use-logging","title":"Use Logging","text":"
    #define DEBUG_MODE\n\n#ifdef DEBUG_MODE\n    #define DEBUG_LOG(x) Serial.println(x)\n#else\n    #define DEBUG_LOG(x)\n#endif\n\n// Usage\nDEBUG_LOG(\"Entity created\");\nDEBUG_LOG(\"Collision detected\");\n
    "},{"location":"resources/troubleshooting/#getting-help","title":"Getting Help","text":"

    If you can't resolve an issue:

    1. Check documentation: Review relevant guides
    2. Search examples: Look at example games
    3. Review code: Check engine source code
    4. Community: Ask on Discord or GitHub
    5. Report issue: Create detailed bug report
    "},{"location":"resources/troubleshooting/#bug-report-template","title":"Bug Report Template","text":"

    When reporting issues, include:

    • Platform: ESP32 or Native
    • Hardware: Display type, ESP32 model
    • Code: Minimal reproduction code
    • Error messages: Full error output
    • Expected behavior: What should happen
    • Actual behavior: What actually happens
    • Steps to reproduce: How to trigger the issue
    "},{"location":"resources/troubleshooting/#see-also","title":"See Also","text":"
    • Limitations and Considerations - Known limitations
    • Performance Tuning - Performance optimization
    • Memory Management - Memory optimization
    • FAQ - Frequently asked questions

    Note: Many issues are configuration-related. Double-check your setup before assuming a bug.

    "},{"location":"tools/sprite_compiler/advanced_features/","title":"Sprite Compiler Advanced Features","text":"

    Advanced features and options for the PixelRoot32 Sprite Compiler to optimize sprite conversion and handle complex scenarios.

    "},{"location":"tools/sprite_compiler/advanced_features/#automatic-palette-detection","title":"Automatic Palette Detection","text":"

    The Sprite Compiler automatically detects if your sprite uses one of the engine's built-in palettes. This simplifies your workflow and ensures color consistency.

    "},{"location":"tools/sprite_compiler/advanced_features/#predefined-engine-palettes","title":"Predefined Engine Palettes","text":"

    The engine includes 5 optimized palettes: - PR32 (Default PixelRoot32 palette) - NES (Nintendo style) - GB (GameBoy style) - GBC (GameBoy Color style) - PICO8 (Fantasy console style)

    "},{"location":"tools/sprite_compiler/advanced_features/#how-it-works","title":"How it works","text":"
    1. Detection: When you compile an image, the tool compares all unique colors found in the sprite with the colors in the 5 engine palettes.
    2. Match: If all colors in your sprite belong to one of these palettes, the compiler:
    3. Omits generating a color array in the header.
    4. Assumes you will use the engine's built-in palette definitions at runtime.
    5. Custom Palette: If your sprite uses colors not found in the engine palettes, it automatically generates a {PREFIX}_PALETTE_MAPPING[16] array in the header file.
    "},{"location":"tools/sprite_compiler/advanced_features/#naming-with-prefixes","title":"Naming with Prefixes","text":"

    You can organize your generated code by using the --prefix parameter (or the Prefix field in the GUI).

    "},{"location":"tools/sprite_compiler/advanced_features/#using-prefixes","title":"Using Prefixes","text":"

    By default, sprites are named SPRITE_N_.... Using a prefix allows you to create more descriptive names and avoid naming collisions.

    python main.py sheet.png --grid 16x16 --sprite 0,0,1,1 --prefix PLAYER_JUM\n

    Generated names will follow this pattern: - PLAYER_JUM_SPRITE_0_LAYER_0 - PLAYER_JUM_SPRITE_0_2BPP - PLAYER_JUM_SPRITE_0_4BPP - PLAYER_JUM_PALETTE_MAPPING (if a custom palette is used)

    "},{"location":"tools/sprite_compiler/advanced_features/#export-modes","title":"Export Modes","text":""},{"location":"tools/sprite_compiler/advanced_features/#layered-1bpp","title":"Layered (1bpp)","text":"

    Best for standard PixelRoot32 rendering. It extracts each color into its own bitmask (1bpp). The engine then renders these layers sequentially.

    "},{"location":"tools/sprite_compiler/advanced_features/#packed-2bpp-4bpp","title":"Packed (2bpp / 4bpp)","text":"

    Generates a single packed array where each pixel uses multiple bits. - 2bpp: 4 colors max (Index 0 is always transparent). - 4bpp: 16 colors max (Index 0 is always transparent).

    These modes are more efficient for sprites with many colors as they require only a single draw call.

    "},{"location":"tools/sprite_compiler/installation/","title":"Sprite Compiler Installation","text":"

    This guide walks you through installing the PixelRoot32 Sprite Compiler on your system.

    "},{"location":"tools/sprite_compiler/installation/#prerequisites","title":"Prerequisites","text":""},{"location":"tools/sprite_compiler/installation/#required-software","title":"Required Software","text":"
    • Python: Version 3.8 or higher
    • pip: Usually included with Python
    "},{"location":"tools/sprite_compiler/installation/#verify-prerequisites","title":"Verify Prerequisites","text":"

    Check if Python is installed:

    python --version\n# Should show 3.8.0 or higher\n

    Check if pip is installed:

    pip --version\n# Should show version number\n

    If not installed, download from python.org

    "},{"location":"tools/sprite_compiler/installation/#installation-methods","title":"Installation Methods","text":""},{"location":"tools/sprite_compiler/installation/#method-1-from-source-recommended","title":"Method 1: From Source (Recommended)","text":""},{"location":"tools/sprite_compiler/installation/#step-1-clone-repository","title":"Step 1: Clone Repository","text":"
    git clone https://github.com/Gperez88/PixelRoot32-Sprite-Compiler.git\ncd PixelRoot32-Sprite-Compiler\n
    "},{"location":"tools/sprite_compiler/installation/#step-2-install-dependencies","title":"Step 2: Install Dependencies","text":"
    pip install -r requirements.txt\n
    "},{"location":"tools/sprite_compiler/installation/#step-3-verify-installation","title":"Step 3: Verify Installation","text":"
    python main.py --help\n
    "},{"location":"tools/sprite_compiler/installation/#method-2-standalone-executable-windows","title":"Method 2: Standalone Executable (Windows)","text":"

    If you are on Windows, you can download the latest standalone .exe from the Releases section of the repository. This does not require Python or any dependencies to be installed.

    "},{"location":"tools/sprite_compiler/installation/#platform-specific-instructions","title":"Platform-Specific Instructions","text":""},{"location":"tools/sprite_compiler/installation/#windows","title":"Windows","text":""},{"location":"tools/sprite_compiler/installation/#using-npm-recommended","title":"Using npm (Recommended)","text":"
    1. Install Node.js from nodejs.org
    2. Download the Windows installer
    3. Run the installer
    4. Restart your terminal/command prompt

    5. Open Command Prompt or PowerShell

    6. Install globally:

      npm install -g pr32-sprite-compiler\n

    7. Verify:

      pr32-sprite-compiler --version\n

    "},{"location":"tools/sprite_compiler/installation/#troubleshooting-windows-issues","title":"Troubleshooting Windows Issues","text":"

    \"pr32-sprite-compiler is not recognized\": - Ensure Node.js is in your PATH - Restart terminal after installation - Try using full path: C:\\Users\\YourName\\AppData\\Roaming\\npm\\pr32-sprite-compiler.cmd

    Permission errors: - Run terminal as Administrator - Or install locally: npm install pr32-sprite-compiler (without -g)

    "},{"location":"tools/sprite_compiler/installation/#linux","title":"Linux","text":""},{"location":"tools/sprite_compiler/installation/#using-npm","title":"Using npm","text":"
    1. Install Node.js (if not already installed):

    Ubuntu/Debian:

    curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -\nsudo apt-get install -y nodejs\n

    Fedora/RHEL:

    sudo dnf install nodejs npm\n

    1. Install Sprite Compiler:

      sudo npm install -g pr32-sprite-compiler\n

    2. Verify:

      pr32-sprite-compiler --version\n

    "},{"location":"tools/sprite_compiler/installation/#troubleshooting-linux-issues","title":"Troubleshooting Linux Issues","text":"

    Permission denied: - Use sudo for global installation - Or install locally without -g flag

    Command not found: - Check npm global bin path: npm config get prefix - Add to PATH if needed: export PATH=$PATH:$(npm config get prefix)/bin

    "},{"location":"tools/sprite_compiler/installation/#macos","title":"macOS","text":""},{"location":"tools/sprite_compiler/installation/#using-npm_1","title":"Using npm","text":"
    1. Install Node.js:
    2. Download from nodejs.org
    3. Or use Homebrew: brew install node

    4. Install Sprite Compiler:

      npm install -g pr32-sprite-compiler\n

    5. Verify:

      pr32-sprite-compiler --version\n

    "},{"location":"tools/sprite_compiler/installation/#using-homebrew-alternative","title":"Using Homebrew (Alternative)","text":"

    If available as a Homebrew formula:

    brew install pr32-sprite-compiler\n

    "},{"location":"tools/sprite_compiler/installation/#gui-version-installation","title":"GUI Version Installation","text":"

    If a GUI version is available:

    "},{"location":"tools/sprite_compiler/installation/#windows_1","title":"Windows","text":"

    Download the installer from the releases page and run it.

    "},{"location":"tools/sprite_compiler/installation/#linux_1","title":"Linux","text":"
    # Download AppImage or .deb package\n# Make executable and run\nchmod +x pr32-sprite-compiler-gui.AppImage\n./pr32-sprite-compiler-gui.AppImage\n
    "},{"location":"tools/sprite_compiler/installation/#macos_1","title":"macOS","text":"

    Download the .dmg file from releases and install.

    "},{"location":"tools/sprite_compiler/installation/#verification","title":"Verification","text":"

    After installation, verify everything works:

    "},{"location":"tools/sprite_compiler/installation/#test-basic-functionality","title":"Test Basic Functionality","text":"
    1. Create a test image:
    2. Create an 8x8 pixel PNG image (black and white)
    3. Save as test.png

    4. Run compiler:

      pr32-sprite-compiler test.png test_output.h\n

    5. Check output:

    6. File test_output.h should be created
    7. Should contain sprite data arrays
    "},{"location":"tools/sprite_compiler/installation/#check-version","title":"Check Version","text":"
    pr32-sprite-compiler --version\n
    "},{"location":"tools/sprite_compiler/installation/#check-help","title":"Check Help","text":"
    pr32-sprite-compiler --help\n
    "},{"location":"tools/sprite_compiler/installation/#updating","title":"Updating","text":""},{"location":"tools/sprite_compiler/installation/#update-via-npm","title":"Update via npm","text":"
    npm update -g pr32-sprite-compiler\n
    "},{"location":"tools/sprite_compiler/installation/#update-from-source","title":"Update from Source","text":"
    cd pr32-sprite-compiler\ngit pull\nnpm install\n
    "},{"location":"tools/sprite_compiler/installation/#uninstallation","title":"Uninstallation","text":""},{"location":"tools/sprite_compiler/installation/#remove-global-installation","title":"Remove Global Installation","text":"
    npm uninstall -g pr32-sprite-compiler\n
    "},{"location":"tools/sprite_compiler/installation/#remove-local-installation","title":"Remove Local Installation","text":"
    npm uninstall pr32-sprite-compiler\n
    "},{"location":"tools/sprite_compiler/installation/#troubleshooting","title":"Troubleshooting","text":""},{"location":"tools/sprite_compiler/installation/#common-issues","title":"Common Issues","text":"

    \"Command not found\" after installation: - Restart your terminal - Check npm global bin path: npm config get prefix - Verify PATH includes npm bin directory

    Permission errors: - On Linux/macOS: Use sudo for global install - Or install locally without -g flag - On Windows: Run terminal as Administrator

    Module not found errors: - Reinstall: npm install -g pr32-sprite-compiler - Clear npm cache: npm cache clean --force

    Version conflicts: - Check Node.js version: node --version - Update Node.js if version is too old - Use nvm (Node Version Manager) to manage versions

    "},{"location":"tools/sprite_compiler/installation/#getting-help","title":"Getting Help","text":"
    • Check the Usage Guide for usage examples
    • Review Troubleshooting for common issues
    • Open an issue on GitHub if problems persist
    "},{"location":"tools/sprite_compiler/installation/#next-steps","title":"Next Steps","text":"

    Once installed, proceed to: - Usage Guide - Learn how to use the compiler - Advanced Features - Explore advanced options

    "},{"location":"tools/sprite_compiler/installation/#see-also","title":"See Also","text":"
    • Overview - What the Sprite Compiler does
    • Available Tools - All PixelRoot32 tools
    "},{"location":"tools/sprite_compiler/overview/","title":"Sprite Compiler Overview","text":"

    The Sprite Compiler is a tool that converts PNG images into PixelRoot32 sprite data formats. It provides both a graphical interface (GUI) and a command-line interface (CLI) to automate the process of creating sprite arrays from image files.

    "},{"location":"tools/sprite_compiler/overview/#what-it-does","title":"What It Does","text":"

    The Sprite Compiler takes bitmap images (PNG) and converts them into C header files containing:

    • Sprite data arrays: Optimized uint16_t arrays for various formats.
    • Layered support: Generates multiple 1bpp layers for complex sprites.
    • Packed formats: Supports 2bpp and 4bpp packed formats.
    • Sprite sheets: Handles grid-based sprite sheets with auto-detection.
    "},{"location":"tools/sprite_compiler/overview/#key-features","title":"Key Features","text":""},{"location":"tools/sprite_compiler/overview/#format-support","title":"\u2705 Format Support","text":"
    • Layered (1bpp): Standard format, generates one array per color.
    • 2bpp (4 colors): Packed format, 2 bits per pixel.
    • 4bpp (16 colors): Packed format, 4 bits per pixel.
    "},{"location":"tools/sprite_compiler/overview/#gui-cli","title":"\u2705 GUI & CLI","text":"
    • Modern GUI: Step-by-step card-based interface for easy configuration.
    • Powerful CLI: Perfect for build scripts and automation.
    "},{"location":"tools/sprite_compiler/overview/#sprite-sheets","title":"\u2705 Sprite Sheets","text":"

    Automatically split sprite sheets into individual sprites:

    python main.py sheet.png --grid 16x16 --sprite 0,0,1,1 --sprite 1,0,1,1 --out output.h\n

    "},{"location":"tools/sprite_compiler/overview/#gui-interface","title":"GUI Interface","text":"

    The GUI is designed to be intuitive and follows a 5-step process:

    1. Input Image: Select your PNG source.
    2. Grid Settings: Define the cell size and offsets.
    3. Sprite Selection: Pick which cells to export.
    4. Export Settings: Choose the mode (Layered, 2bpp, 4bpp), set a Prefix, and choose the output path.
    5. About: Quick access to version info and credits (via the ? button).
    6. Log: Technical feedback and performance alerts.
    "},{"location":"tools/sprite_compiler/overview/#input-requirements","title":"Input Requirements","text":""},{"location":"tools/sprite_compiler/overview/#supported-formats","title":"Supported Formats","text":"
    • PNG: Primary format (recommended)
    • Indexed color PNG: Best for 1bpp conversion
    • Grayscale PNG: Automatically converted to 1bpp
    • RGB PNG: Converted using threshold or palette
    "},{"location":"tools/sprite_compiler/overview/#image-constraints","title":"Image Constraints","text":"

    For 1bpp sprites: - Maximum width: 16 pixels - Height: Any (typically 8, 16, 32 pixels) - Colors: Black and white (or converted automatically)

    For 2bpp sprites: - Maximum width: 16 pixels - Colors: Up to 4 colors

    For 4bpp sprites: - Maximum width: 16 pixels - Colors: Up to 16 colors

    "},{"location":"tools/sprite_compiler/overview/#output-format","title":"Output Format","text":"

    The compiler generates C header files with optimized arrays:

    // Generated by PixelRoot32 Sprite Compiler\n\n// Optional palette mapping if using custom colors\nstatic const Color PLAYER_PALETTE_MAPPING[16] = {\n    (Color)0, (Color)1, (Color)2, (Color)3,\n    // ...\n};\n\n// Sprite data array (4bpp example)\nstatic const uint16_t PLAYER_SPRITE_0_4BPP[] = {\n    0x0000, 0x1234, 0x5678, // Row 0\n    // ... more rows\n};\n
    "},{"location":"tools/sprite_compiler/overview/#use-cases","title":"Use Cases","text":""},{"location":"tools/sprite_compiler/overview/#1-single-sprite-conversion","title":"1. Single Sprite Conversion","text":"

    Convert a single image to a sprite:

    pr32-sprite-compiler player.png player_sprite.h --name PLAYER_SPRITE\n

    "},{"location":"tools/sprite_compiler/overview/#2-animation-frames","title":"2. Animation Frames","text":"

    Convert multiple frames for animation:

    pr32-sprite-compiler --batch walk_*.png --output-dir animations/ --prefix WALK_\n

    "},{"location":"tools/sprite_compiler/overview/#3-sprite-sheet-processing","title":"3. Sprite Sheet Processing","text":"

    Split a sprite sheet into individual sprites:

    pr32-sprite-compiler characters.png output.h --sheet 16x16 --count 8\n

    "},{"location":"tools/sprite_compiler/overview/#4-batch-asset-processing","title":"4. Batch Asset Processing","text":"

    Process entire asset directories:

    pr32-sprite-compiler --batch assets/sprites/*.png --output-dir src/sprites/\n

    "},{"location":"tools/sprite_compiler/overview/#workflow-integration","title":"Workflow Integration","text":""},{"location":"tools/sprite_compiler/overview/#typical-development-workflow","title":"Typical Development Workflow","text":"
    1. Create sprites in your image editor (Aseprite, Piskel, GIMP, etc.)
    2. Save as PNG with appropriate dimensions
    3. Run compiler to generate header files
    4. Include headers in your PixelRoot32 project
    5. Use sprites in your game code
    "},{"location":"tools/sprite_compiler/overview/#automation-example","title":"Automation Example","text":"
    #!/bin/bash\n# build-sprites.sh\n\n# Compile all sprites\npr32-sprite-compiler assets/sprites/*.png --output-dir src/sprites/\n\n# Continue with your build process\nplatformio run\n
    "},{"location":"tools/sprite_compiler/overview/#advantages-over-manual-creation","title":"Advantages Over Manual Creation","text":""},{"location":"tools/sprite_compiler/overview/#time-saving","title":"\u2705 Time Saving","text":"
    • No manual bit pattern conversion
    • Automatic format optimization
    • Batch processing multiple sprites
    "},{"location":"tools/sprite_compiler/overview/#accuracy","title":"\u2705 Accuracy","text":"
    • Correct bit ordering
    • Proper array formatting
    • Valid C++ syntax
    "},{"location":"tools/sprite_compiler/overview/#consistency","title":"\u2705 Consistency","text":"
    • Uniform naming conventions
    • Standardized output format
    • Consistent code structure
    "},{"location":"tools/sprite_compiler/overview/#maintainability","title":"\u2705 Maintainability","text":"
    • Easy to regenerate from source images
    • Version control friendly
    • Clear separation of assets and code
    "},{"location":"tools/sprite_compiler/overview/#limitations","title":"Limitations","text":"
    • Width limit: 16 pixels for 1bpp (hardware constraint)
    • Color depth: Limited by format (1bpp = 2 colors, 2bpp = 4 colors, etc.)
    • File format: Primarily PNG (other formats may require conversion)
    "},{"location":"tools/sprite_compiler/overview/#next-steps","title":"Next Steps","text":"
    • Installation Guide - Set up the compiler
    • Usage Guide - Learn how to use it
    • Advanced Features - Explore advanced options
    "},{"location":"tools/sprite_compiler/overview/#see-also","title":"See Also","text":"
    • Manual - Sprites and Animation - Using sprites in games
    • Code Examples - Sprites - Sprite usage examples
    • Available Tools - All PixelRoot32 tools
    "},{"location":"tools/sprite_compiler/usage_guide/","title":"Sprite Compiler Usage Guide","text":"

    Complete guide to using the PixelRoot32 Sprite Compiler for converting images to sprite data.

    "},{"location":"tools/sprite_compiler/usage_guide/#basic-usage","title":"Basic Usage","text":""},{"location":"tools/sprite_compiler/usage_guide/#launching-the-gui","title":"Launching the GUI","text":"

    The easiest way to use the compiler is via the Graphical User Interface (GUI).

    python main.py\n

    This will open the application where you can interactively load images, configure the grid, and export sprites.

    "},{"location":"tools/sprite_compiler/usage_guide/#command-line-interface-cli","title":"Command Line Interface (CLI)","text":"

    For automation, you can use the CLI mode by passing arguments to the script.

    python main.py [input] [options]\n

    Required:

    • input: Input PNG image file
    • --grid WxH: Grid cell size (e.g., 16x16)
    • --sprite gx,gy,gw,gh: Sprite definition (can be repeated)

    Optional:

    • --prefix NAME: Prefix for generated arrays (e.g., PLAYER_JUM)
    • --out FILE: Output header file (default: sprites.h)
    • --offset X,Y: Initial offset in pixels (default: 0,0)
    • --mode MODE: Export mode (layered, 2bpp, 4bpp)
    "},{"location":"tools/sprite_compiler/usage_guide/#cli-examples","title":"CLI Examples","text":""},{"location":"tools/sprite_compiler/usage_guide/#simple-conversion","title":"Simple Conversion","text":"

    Convert a single 16x16 sprite located at the top-left corner:

    python main.py player.png --grid 16x16 --sprite 0,0,1,1 --out player.h\n
    "},{"location":"tools/sprite_compiler/usage_guide/#multiple-sprites","title":"Multiple Sprites","text":"

    Convert multiple sprites from a single sheet:

    python main.py sheet.png --grid 16x16 \\\n  --sprite 0,0,1,1 \\\n  --sprite 1,0,1,1 \\\n  --sprite 2,0,1,1 \\\n  --out animations.h\n
    "},{"location":"tools/sprite_compiler/usage_guide/#export-modes","title":"Export Modes","text":"

    Layered (Default): Generates multiple uint16_t arrays, one for each color layer. Best for standard PixelRoot32 rendering.

    python main.py icon.png --grid 16x16 --sprite 0,0,1,1 --mode layered\n

    Packed 2bpp: Generates a single array with 2 bits per pixel (4 colors max).

    python main.py icon.png --grid 16x16 --sprite 0,0,1,1 --mode 2bpp\n
    "},{"location":"tools/sprite_compiler/usage_guide/#step-by-step-examples","title":"Step-by-Step Examples","text":""},{"location":"tools/sprite_compiler/usage_guide/#example-1-simple-player-sprite","title":"Example 1: Simple Player Sprite","text":"

    Step 1: Create Image

    • Create an 8x8 pixel PNG image
    • Use black and white colors
    • Save as player.png

    Step 2: Compile

    python main.py player.png --grid 8x8 --sprite 0,0,1,1 --prefix PLAYER --out player_sprite.h\n

    Step 3: Use in Code

    #include \"player_sprite.h\"\n\nvoid draw() {\n    // PLAYER_SPRITE_0_LAYER_0 is generated automatically\n    renderer.drawSprite(PLAYER_SPRITE_0_LAYER_0, 100, 100, Color::White);\n}\n
    "},{"location":"tools/sprite_compiler/usage_guide/#example-2-multiple-animation-frames","title":"Example 2: Multiple Animation Frames","text":"

    Step 1: Prepare Images

    • Create frames: walk_0.png, walk_1.png, walk_2.png
    • All same size (e.g., 16x16)

    Step 2: Batch Compile

    pr32-sprite-compiler --batch walk_*.png --output-dir animations/ --prefix WALK_\n

    Step 3: Use in Animation

    #include \"animations/walk_0.h\"\n#include \"animations/walk_1.h\"\n#include \"animations/walk_2.h\"\n\nconst Sprite* WALK_FRAMES[] = {\n    &WALK_0_SPRITE,\n    &WALK_1_SPRITE,\n    &WALK_2_SPRITE\n};\n
    "},{"location":"tools/sprite_compiler/usage_guide/#example-3-sprite-sheet","title":"Example 3: Sprite Sheet","text":"

    Step 1: Create Sprite Sheet

    • Create a 64x64 image with 4x4 grid of 16x16 sprites
    • Save as characters.png

    Step 2: Split Sheet

    pr32-sprite-compiler characters.png characters.h --sheet 16x16 --count 16\n

    Step 3: Use Individual Sprites

    #include \"characters.h\"\n\n// Sprites named CHARACTER_0, CHARACTER_1, etc.\nrenderer.drawSprite(CHARACTER_0, 50, 50, Color::White);\nrenderer.drawSprite(CHARACTER_1, 70, 50, Color::White);\n
    "},{"location":"tools/sprite_compiler/usage_guide/#batch-processing","title":"Batch Processing","text":""},{"location":"tools/sprite_compiler/usage_guide/#process-multiple-files","title":"Process Multiple Files","text":"

    Process all PNG files in a directory:

    pr32-sprite-compiler --batch sprites/*.png --output-dir generated/\n
    "},{"location":"tools/sprite_compiler/usage_guide/#with-options","title":"With Options","text":"

    Apply options to all files:

    pr32-sprite-compiler --batch assets/*.png \\\n    --output-dir src/sprites/ \\\n    --format 1bpp \\\n    --prefix SPRITE_\n
    "},{"location":"tools/sprite_compiler/usage_guide/#recursive-processing","title":"Recursive Processing","text":"

    Process subdirectories:

    pr32-sprite-compiler --batch assets/**/*.png --output-dir generated/\n
    "},{"location":"tools/sprite_compiler/usage_guide/#sprite-sheets","title":"Sprite Sheets","text":""},{"location":"tools/sprite_compiler/usage_guide/#automatic-splitting","title":"Automatic Splitting","text":"

    Split a sprite sheet into individual sprites:

    pr32-sprite-compiler sheet.png output.h --sheet 8x8 --count 16\n

    Parameters:

    • --sheet WxH: Tile size (width x height)
    • --count N: Number of sprites in sheet
    "},{"location":"tools/sprite_compiler/usage_guide/#grid-layout","title":"Grid Layout","text":"

    Specify grid dimensions:

    pr32-sprite-compiler sheet.png output.h \\\n    --sheet 16x16 \\\n    --grid 4x4 \\\n    --count 16\n

    Parameters:

    • --grid WxH: Grid dimensions (columns x rows)
    "},{"location":"tools/sprite_compiler/usage_guide/#custom-naming","title":"Custom Naming","text":"

    Name sprites with index:

    pr32-sprite-compiler sheet.png output.h \\\n    --sheet 8x8 \\\n    --count 8 \\\n    --prefix CHARACTER_ \\\n    --indexed\n

    Generates: CHARACTER_0, CHARACTER_1, etc.

    "},{"location":"tools/sprite_compiler/usage_guide/#custom-palettes","title":"Custom Palettes","text":""},{"location":"tools/sprite_compiler/usage_guide/#using-palette-file","title":"Using Palette File","text":"

    Convert with custom color palette:

    pr32-sprite-compiler sprite.png output.h --palette palette.json\n

    Palette JSON format:

    {\n  \"colors\": [\n    {\"r\": 0, \"g\": 0, \"b\": 0, \"name\": \"black\"},\n    {\"r\": 255, \"g\": 255, \"b\": 255, \"name\": \"white\"}\n  ]\n}\n
    "},{"location":"tools/sprite_compiler/usage_guide/#built-in-palettes","title":"Built-in Palettes","text":"

    Use predefined palettes:

    pr32-sprite-compiler sprite.png output.h --palette nes\npr32-sprite-compiler sprite.png output.h --palette gb\npr32-sprite-compiler sprite.png output.h --palette pico8\n
    "},{"location":"tools/sprite_compiler/usage_guide/#advanced-options","title":"Advanced Options","text":""},{"location":"tools/sprite_compiler/usage_guide/#threshold-for-grayscale","title":"Threshold for Grayscale","text":"

    Set threshold for black/white conversion:

    pr32-sprite-compiler sprite.png output.h --threshold 128\n

    Values: 0-255 (default: 127)

    "},{"location":"tools/sprite_compiler/usage_guide/#dithering","title":"Dithering","text":"

    Enable dithering for better gradients:

    pr32-sprite-compiler sprite.png output.h --dither\n
    "},{"location":"tools/sprite_compiler/usage_guide/#alignment","title":"Alignment","text":"

    Control output alignment:

    pr32-sprite-compiler sprite.png output.h --align 4\n
    "},{"location":"tools/sprite_compiler/usage_guide/#endianness","title":"Endianness","text":"

    Specify byte order:

    pr32-sprite-compiler sprite.png output.h --endian little\npr32-sprite-compiler sprite.png output.h --endian big\n
    "},{"location":"tools/sprite_compiler/usage_guide/#output-customization","title":"Output Customization","text":""},{"location":"tools/sprite_compiler/usage_guide/#namespace","title":"Namespace","text":"

    Wrap output in namespace:

    pr32-sprite-compiler sprite.png output.h --namespace MyGame\n
    "},{"location":"tools/sprite_compiler/usage_guide/#header-guard","title":"Header Guard","text":"

    Custom header guard:

    pr32-sprite-compiler sprite.png output.h --guard MY_SPRITE_H\n
    "},{"location":"tools/sprite_compiler/usage_guide/#include-paths","title":"Include Paths","text":"

    Custom include paths:

    pr32-sprite-compiler sprite.png output.h \\\n    --include \"<graphics/Sprite.h>\" \\\n    --include \"<stdint.h>\"\n
    "},{"location":"tools/sprite_compiler/usage_guide/#integration-with-build-systems","title":"Integration with Build Systems","text":""},{"location":"tools/sprite_compiler/usage_guide/#platformio","title":"PlatformIO","text":"

    Add to platformio.ini:

    [env:esp32dev]\nextra_scripts = \n    pre:scripts/compile_sprites.py\n

    compile_sprites.py:

    Import(\"env\")\nimport subprocess\n\nsubprocess.run([\n    \"pr32-sprite-compiler\",\n    \"--batch\", \"assets/sprites/*.png\",\n    \"--output-dir\", \"src/sprites/\"\n])\n
    "},{"location":"tools/sprite_compiler/usage_guide/#makefile","title":"Makefile","text":"
    SPRITES = $(wildcard assets/sprites/*.png)\nSPRITE_HEADERS = $(SPRITES:assets/sprites/%.png=src/sprites/%.h)\n\nsrc/sprites/%.h: assets/sprites/%.png\n pr32-sprite-compiler $< $@ --name $(shell basename $< .png | tr '[:lower:]' '[:upper:]')_SPRITE\n\nsprites: $(SPRITE_HEADERS)\n
    "},{"location":"tools/sprite_compiler/usage_guide/#cmake","title":"CMake","text":"
    file(GLOB SPRITE_FILES \"assets/sprites/*.png\")\n\nforeach(SPRITE ${SPRITE_FILES})\n    get_filename_component(SPRITE_NAME ${SPRITE} NAME_WE)\n    add_custom_command(\n        OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/sprites/${SPRITE_NAME}.h\n        COMMAND pr32-sprite-compiler\n        ARGS ${SPRITE} ${CMAKE_CURRENT_SOURCE_DIR}/src/sprites/${SPRITE_NAME}.h\n        DEPENDS ${SPRITE}\n    )\nendforeach()\n
    "},{"location":"tools/sprite_compiler/usage_guide/#gui-usage-if-available","title":"GUI Usage (If Available)","text":""},{"location":"tools/sprite_compiler/usage_guide/#opening-gui","title":"Opening GUI","text":"
    pr32-sprite-compiler --gui\n

    Or launch the GUI application directly.

    "},{"location":"tools/sprite_compiler/usage_guide/#gui-workflow","title":"GUI Workflow","text":"
    1. Drag and drop images into the window
    2. Preview sprite data in real-time
    3. Adjust settings visually (format, threshold, etc.)
    4. Export to header files
    5. Batch process multiple files
    "},{"location":"tools/sprite_compiler/usage_guide/#gui-features","title":"GUI Features","text":"
    • Visual preview of sprite conversion
    • Real-time threshold adjustment
    • Palette selection
    • Batch processing interface
    • Export options
    "},{"location":"tools/sprite_compiler/usage_guide/#best-practices","title":"Best Practices","text":""},{"location":"tools/sprite_compiler/usage_guide/#image-preparation","title":"Image Preparation","text":"
    • Use indexed color PNG for best results
    • Keep sprites small (8x8, 16x16, 32x32)
    • Use black and white for 1bpp
    • Limit colors for 2bpp/4bpp formats
    "},{"location":"tools/sprite_compiler/usage_guide/#file-organization","title":"File Organization","text":"
    project/\n\u251c\u2500\u2500 assets/\n\u2502   \u2514\u2500\u2500 sprites/\n\u2502       \u251c\u2500\u2500 player.png\n\u2502       \u251c\u2500\u2500 enemy.png\n\u2502       \u2514\u2500\u2500 items.png\n\u251c\u2500\u2500 src/\n\u2502   \u2514\u2500\u2500 sprites/          # Generated headers\n\u2502       \u251c\u2500\u2500 player.h\n\u2502       \u251c\u2500\u2500 enemy.h\n\u2502       \u2514\u2500\u2500 items.h\n\u2514\u2500\u2500 platformio.ini\n
    "},{"location":"tools/sprite_compiler/usage_guide/#naming-conventions","title":"Naming Conventions","text":"
    • Use descriptive names: player_walk_0.png \u2192 PLAYER_WALK_0_SPRITE
    • Be consistent: All caps for sprite names
    • Use prefixes: ENEMY_, PLAYER_, ITEM_
    "},{"location":"tools/sprite_compiler/usage_guide/#version-control","title":"Version Control","text":"
    • Commit generated headers (they're part of the build)
    • Or add to .gitignore and regenerate on build
    • Keep source images in version control
    "},{"location":"tools/sprite_compiler/usage_guide/#troubleshooting","title":"Troubleshooting","text":""},{"location":"tools/sprite_compiler/usage_guide/#common-issues","title":"Common Issues","text":"

    \"Image too large\":

    • Sprites must be \u2264 16 pixels wide for 1bpp
    • Resize image or split into multiple sprites

    \"Colors not converting correctly\":

    • Use indexed color PNG
    • For 1bpp: Use only black and white
    • For 2bpp: Use exactly 4 colors
    • For 4bpp: Use up to 16 colors

    \"Output file not found\":

    • Check write permissions
    • Verify output directory exists
    • Use absolute paths if needed

    \"Invalid format\":

    • Ensure input is PNG format
    • Check file is not corrupted
    • Try re-saving image in image editor
    "},{"location":"tools/sprite_compiler/usage_guide/#getting-help","title":"Getting Help","text":"
    pr32-sprite-compiler --help\n

    Shows all available options and usage.

    "},{"location":"tools/sprite_compiler/usage_guide/#next-steps","title":"Next Steps","text":"
    • Advanced Features - Explore advanced options
    • Overview - Learn more about the compiler
    • Manual - Sprites - Using sprites in games
    "},{"location":"tools/sprite_compiler/usage_guide/#see-also","title":"See Also","text":"
    • Code Examples - Sprites - Sprite usage examples
    • Troubleshooting - Common issues and solutions
    "},{"location":"tools/tilemap_editor/installation/","title":"Installation Guide","text":"

    The PixelRoot32 Tilemap Editor can be run directly from source or as a standalone executable on Windows.

    "},{"location":"tools/tilemap_editor/installation/#1-requirements","title":"1. Requirements","text":"
    • Python 3.13+ (if running from source).
    • Windows 10/11 (recommended).
    "},{"location":"tools/tilemap_editor/installation/#2-install-from-source","title":"2. Install from Source","text":""},{"location":"tools/tilemap_editor/installation/#21-clone-the-repository","title":"2.1 Clone the Repository","text":"
    git clone https://github.com/Gperez88/PixelRoot32-Tilemap-Editor.git\ncd PixelRoot32-Tilemap-Editor\n
    "},{"location":"tools/tilemap_editor/installation/#22-install-dependencies","title":"2.2 Install Dependencies","text":"

    The editor uses several Python libraries for the GUI and image processing:

    pip install ttkbootstrap pillow jinja2\n
    "},{"location":"tools/tilemap_editor/installation/#23-run-the-editor","title":"2.3 Run the Editor","text":"
    python main.py\n
    "},{"location":"tools/tilemap_editor/installation/#3-standalone-executable-windows","title":"3. Standalone Executable (Windows)","text":"

    For a more convenient experience, you can use the pre-compiled version:

    1. Go to the Releases section of the repository.
    2. Download the latest PixelRoot32-Editor-win64.zip.
    3. Extract the contents to a folder.
    4. Run PixelRoot32-Editor.exe.

    Note: No Python installation is required to run the standalone executable.

    "},{"location":"tools/tilemap_editor/installation/#4-building-your-own-executable","title":"4. Building your own Executable","text":"

    If you want to package the editor yourself:

    1. Install PyInstaller:
    pip install pyinstaller\n
    1. Run the build command using the provided .spec file:
    pyinstaller pixelroot32_editor.spec\n
    1. The executable will be available in the dist/ folder.
    "},{"location":"tools/tilemap_editor/overview/","title":"Tilemap Editor Overview","text":"

    The PixelRoot32 Tilemap Editor is a powerful visual tool designed to create complex multi-layered tile-based maps for the PixelRoot32 engine. It simplifies the process of designing game environments, managing tilesets, and exporting optimized C++ code.

    "},{"location":"tools/tilemap_editor/overview/#what-it-does","title":"What It Does","text":"

    The Tilemap Editor allows you to:

    • Visual Design: Paint tiles directly onto a canvas with layers and transparency.
    • Tileset Management: Import PNG images as tilesets and select single or multiple tiles.
    • Multi-Layer Support: Organize your map into up to 8 layers for parallax effects or depth.
    • Optimized Export: Generate C++ header and source files compatible with the PixelRoot32 renderer.
    • BPP Support: Export maps in 1bpp, 2bpp, or 4bpp formats to balance memory usage and color depth.
    "},{"location":"tools/tilemap_editor/overview/#key-features","title":"Key Features","text":""},{"location":"tools/tilemap_editor/overview/#visual-editing-tools","title":"\u2705 Visual Editing Tools","text":"
    • Brush: Paint individual tiles or patterns.
    • Eraser: Remove tiles from the active layer.
    • Rectangle Fill: Quickly fill areas with a specific tile.
    • Pipette: Pick an existing tile from the canvas.
    "},{"location":"tools/tilemap_editor/overview/#multi-layer-system","title":"\u2705 Multi-Layer System","text":"
    • Visibility Toggle: Hide/show layers to focus on specific parts of the map.
    • Opacity Control: Adjust layer transparency for complex blending effects.
    • Layer Reordering: Change the render order of your tilemaps.
    "},{"location":"tools/tilemap_editor/overview/#tileset-selector","title":"\u2705 Tileset Selector","text":"
    • Smart Selection: Drag and select a rectangular area of tiles.
    • Multiple Tilesets: Support for multiple tilesets per project (planned).
    • Auto-import: Automatically detects tile size from the imported image.
    "},{"location":"tools/tilemap_editor/overview/#engine-integration","title":"\u2705 Engine Integration","text":"
    • Workspace Selection: Link the editor to your PixelRoot32 projects directory.
    • Direct Export: Files are generated with the correct namespaces and structures for immediate use.
    • BPP Compatibility: Ensures exported data matches the engine's expected format for 1bpp, 2bpp, and 4bpp.
    "},{"location":"tools/tilemap_editor/overview/#data-formats","title":"Data Formats","text":""},{"location":"tools/tilemap_editor/overview/#project-file-pr32scene","title":"Project File (.pr32scene)","text":"

    The editor uses a custom JSON-based format to save your project state, including:

    • Tileset metadata (path, tile size, spacing).
    • Layer data (tile indices, width, height, position).
    • Project settings (BPP, namespace).
    "},{"location":"tools/tilemap_editor/overview/#exported-c","title":"Exported C++","text":"

    The editor generates .h and .cpp files containing:

    • Tilemap Data: Packed arrays of tile indices.
    • Tilemap Structures: pixelroot32::graphics::TileMap (or TileMap2bpp/TileMap4bpp) definitions.
    • Scene Headers: Convenient entry points for loading entire maps into your game.
    "},{"location":"tools/tilemap_editor/usage_guide/","title":"Usage Guide","text":"

    This guide covers the basic workflow for creating and exporting a tilemap using the PixelRoot32 Tilemap Editor.

    "},{"location":"tools/tilemap_editor/usage_guide/#1-creating-a-new-project","title":"1. Creating a New Project","text":"
    1. Launch the editor.
    2. Go to File > New Project.
    3. Enter the project name and select the base Color Depth (BPP):
    4. 1bpp: Monochrome (2 colors).
    5. 2bpp: 4 colors.
    6. 4bpp: 16 colors.
    7. Set the Tile Size (e.g., 8x8, 16x16).
    "},{"location":"tools/tilemap_editor/usage_guide/#2-importing-a-tileset","title":"2. Importing a Tileset","text":"
    1. In the Tileset panel, click on Load Tileset.
    2. Select a PNG image containing your tiles.
    3. The image will be sliced into tiles based on the tile size set in the project.
    "},{"location":"tools/tilemap_editor/usage_guide/#3-painting-tiles","title":"3. Painting Tiles","text":"
    1. Select a tile (or a range of tiles) from the Tileset panel.
    2. Select the Brush tool (Shortcut: B).
    3. Click and drag on the canvas to paint.
    4. Use the Layers panel to switch between different layers.
    "},{"location":"tools/tilemap_editor/usage_guide/#4-selection-and-transformations","title":"4. Selection and Transformations","text":"
    • Single Selection: Click on a tile in the tileset.
    • Area Selection: Click and drag in the tileset to select a rectangular block of tiles.
    • Pipette: Press P and click on the canvas to pick the tile under the cursor.
    "},{"location":"tools/tilemap_editor/usage_guide/#5-exporting-to-c","title":"5. Exporting to C++","text":"
    1. Ensure your Workspace Path is correctly set in Project Settings. This should point to your PixelRoot32 project's src or include directory.
    2. Click on File > Export.
    3. The editor will generate:
    4. <ProjectName>_data.h and .cpp: Containing the raw tile data and structures.
    5. <ProjectName>_scene.h: A high-level header to include in your game.
    "},{"location":"tools/tilemap_editor/usage_guide/#6-keyboard-shortcuts","title":"6. Keyboard Shortcuts","text":"Shortcut Action B Brush Tool E Eraser Tool R Rectangle Fill Tool P Pipette Tool Space + Drag Pan Canvas Mouse Wheel Zoom In/Out Ctrl + N New Project Ctrl + S Save Project Ctrl + E Export Project Esc Close floating panels"}]} \ No newline at end of file diff --git a/site/sitemap.xml b/site/sitemap.xml index 45d8876..18ff6f0 100644 --- a/site/sitemap.xml +++ b/site/sitemap.xml @@ -2,270 +2,266 @@ https://pixelroot32-game-engine.github.io/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/audio/audio_config/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/audio/audio_engine/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/audio/audio_types/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/audio/music_player/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/core/actor/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/core/engine/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/core/entity/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/core/input_config/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/core/input_manager/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/core/physics_actor/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/core/scene/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/graphics/camera2d/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/graphics/color/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/graphics/display_config/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/graphics/font/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/graphics/renderer/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/graphics/sprite/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/graphics/tilemap/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/physics/collision_system/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/physics/collision_types/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/ui/ui_button/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/ui/ui_checkbox/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/ui/ui_element/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/ui/ui_label/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/ui/ui_layout/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/ui/ui_layouts/anchor_layout/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/ui/ui_layouts/grid_layout/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/ui/ui_layouts/horizontal_layout/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/ui/ui_layouts/padding_container/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/ui/ui_layouts/panel/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/api_reference/ui/ui_layouts/vertical_layout/ - 2026-01-25 - - - https://pixelroot32-game-engine.github.io/examples/space_invaders/overview/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/getting_started/fundamental_concepts/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/getting_started/installation/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/getting_started/what_is_pixelroot32/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/getting_started/why_pixelroot32/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/getting_started/your_first_project/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/manual/advanced_graphics/cameras_and_scrolling/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/manual/advanced_graphics/color_palettes/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/manual/advanced_graphics/particles_and_effects/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/manual/advanced_graphics/sprites_and_animation/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/manual/advanced_graphics/tilemaps/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/manual/game_development/audio/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/manual/game_development/basic_rendering/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/manual/game_development/input_and_control/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/manual/game_development/physics_and_collisions/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/manual/game_development/scenes_and_entities/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/manual/game_development/user_interface/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/manual/optimization/extensibility/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/manual/optimization/memory_management/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/manual/optimization/performance_tuning/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/manual/optimization/platforms_and_drivers/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/reference/api_overview/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/reference/code_examples/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/reference/game_examples_guide/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/resources/available_tools/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/resources/faq/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/resources/limitations_and_considerations/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/resources/troubleshooting/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/tools/sprite_compiler/advanced_features/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/tools/sprite_compiler/installation/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/tools/sprite_compiler/overview/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/tools/sprite_compiler/usage_guide/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/tools/tilemap_editor/installation/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/tools/tilemap_editor/overview/ - 2026-01-25 + 2026-01-29 https://pixelroot32-game-engine.github.io/tools/tilemap_editor/usage_guide/ - 2026-01-25 + 2026-01-29 \ No newline at end of file diff --git a/site/sitemap.xml.gz b/site/sitemap.xml.gz index f5d928595f0848364a4faba6807b2c81637b1f46..122963a449c21e4bf4dea3b4ad3e1cb11f592612 100644 GIT binary patch literal 858 zcmV-g1Eu^QiwFn+qk3rq|8r?{Wo=<_E_iKh0L_}sZrd;n$M1cLBKO2?1`O-arnf!8 zb`}_kcDTusCeium(~pYXuItj%2AD>W*otibK#J5~wC^8}y}#aLE)>JV;&yejxW-_k zqtHDpzJ2|&{Iq!7T-^`Z7vyW{9LmFD$DBSl&EarZ6=Liml~v4LQ|wL-N;5S!H>@b;Ho3u4cisP=Dw-@U!M+z`{*|7 zoAt-#&Fyl1Ctaqt@qW$A4gc-dkjP|gF^g5Ofx*?b`fY3sEp{0{L+gh^ z)+7%n%zD^t%mQdRYGcwiz9uLpJ}HBukOo$lD_)`X5TH{x=+o{b0a=X3!Cx$D8v9*_ zv=du3Ryt=`JB?`3YjWcYm!nd1zPk8)0c>Mng;;Xvq)nqsOUj}?6@oGOvnW@H^IkXwZrilDyQI_VQiB=mpT^-HeN1PiKbF`Pf^DHz{l{X4j(GkHaV*l-@mEEcBp!{BFV)&J z=CoEP+2z;JNCCQ318Jl!4 ziUJwLe`P1#XU<&X5E>P_*s4#Wfb(x<+#>Lh=MBlrh+2vmm+3TQ*UMx>kr{pdbT-0G k>n($h>Kh8WeEJ4Xk4xWx{TGj(e+u&E7e@k(hRi4c0Pi}c!T;NR}M=BTwIzDPPtIxZ4Peb+v`9UU!^1M1Qr%z3DI-NF!7<))%6LZ%Tdyo&MnFpK4 z&0nkCy9asI8%*!U*OQNSSMO5fHi^#YbBz39yY8UJHHMA?ZaU(_u-{N@o|-!U_HroP zN4MKPZa=Oc->od`Vce zq4h%{Ym$cxW<6{+W&yMuwJ~Y)uNjJoFUp`Oq=D7tiW{^Z0(9z}J{>L+ki}?p{$f#+ z?{^u}LDs6VGC9N6X+(=&lNqmEj!Mn>>f-YX*v7yLvE-wZF^wiIDU1442*%_`QLKh+ z&apK(Jyrd0%B6^K*G#BPJPss3W@+J$ijmmpHIa zs?tEPhnST^cA1=VL|H_7k&Pa_a?k{>HioKPVvx(SUeKU5m`iiS%w$y<8_&=uk5x5+ zMML2TqU%Z%h4DyuT9U0p=IU&PAv3zB9fE_pbI8hx16oX6H1Sc8ciscjvRdFZ*XaOk zsF<58b>UsKqDgdS(^4*MQjY(iT{KpE2m|;A+)?&jbmmpvUkn6iip^CkhfXJHZ3CDD zUKV;;8UH3?f|{HqC63F9Z7WvSNjep}6DI`f7f(EIlFpT{hGne(G!BpGV;ZZFsr|1I zY!CHBVrsPLi07XWrv`HMzbXhN@o0p+c9KWTxsRaI7015voOkL`2BAvKpXRto{GT#z5qQYU zhU9%jElrI3bQ-ejeX^m*j=p?4o8abp%b=tB?Fn5reFdk-q_4n!i$%vj1$p-!m@mvl HXej^y_Vuh* diff --git a/site/tools/sprite_compiler/advanced_features/index.html b/site/tools/sprite_compiler/advanced_features/index.html index 59e5f26..d3d8b57 100644 --- a/site/tools/sprite_compiler/advanced_features/index.html +++ b/site/tools/sprite_compiler/advanced_features/index.html @@ -1,104 +1,2 @@ - Advanced Features - PixelRoot32 Documentation

    Sprite Compiler Advanced Features

    Advanced features and options for the PixelRoot32 Sprite Compiler to optimize sprite conversion and handle complex scenarios.

    Dithering

    Dithering improves the visual quality of converted sprites by simulating intermediate colors through pixel patterns.

    Basic Dithering

    Enable dithering for better gradient handling:

    pr32-sprite-compiler sprite.png output.h --dither
    -

    Dithering Strength

    Control dithering intensity:

    pr32-sprite-compiler sprite.png output.h --dither --dither-strength 0.5
    -

    Values: - 0.0: No dithering - 0.5: Moderate dithering (default) - 1.0: Maximum dithering

    Dithering Algorithms

    Choose dithering algorithm:

    # Floyd-Steinberg (default)
    -pr32-sprite-compiler sprite.png output.h --dither --dither-algorithm floyd-steinberg
    -
    -# Ordered (Bayer)
    -pr32-sprite-compiler sprite.png output.h --dither --dither-algorithm ordered
    -
    -# Atkinson
    -pr32-sprite-compiler sprite.png output.h --dither --dither-algorithm atkinson
    -

    When to use: - Floyd-Steinberg: Best for most images, smooth gradients - Ordered: Faster, good for patterns - Atkinson: Softer, less noticeable patterns

    Layer Merging (MultiSprite)

    Combine compatible sprite layers to reduce draw calls and improve performance.

    Basic Layer Merging

    Merge layers with compatible colors:

    pr32-sprite-compiler sprite.png output.h --merge-layers
    -

    Layer Compatibility

    Control which layers can be merged:

    pr32-sprite-compiler sprite.png output.h \
    -    --merge-layers \
    -    --merge-threshold 0.1
    -

    Parameters: - --merge-threshold: Color similarity threshold (0.0-1.0)

    Preserve Layer Colors

    Keep per-layer color assignments:

    pr32-sprite-compiler sprite.png output.h \
    -    --merge-layers \
    -    --preserve-colors
    -

    Bounds & Packing

    Auto-Trim Transparent Borders

    Automatically remove transparent borders:

    pr32-sprite-compiler sprite.png output.h --trim
    -

    Benefits: - Reduces sprite size - Saves memory - Faster rendering

    Manual Bounds

    Specify custom bounds:

    pr32-sprite-compiler sprite.png output.h \
    -    --bounds 2,2,14,14
    -

    Format: left,top,right,bottom (in pixels)

    Sprite Packing

    Pack multiple sprites into pages:

    pr32-sprite-compiler --batch sprites/*.png \
    -    --output-dir packed/ \
    -    --pack \
    -    --pack-size 256x256
    -

    Parameters: - --pack: Enable packing - --pack-size WxH: Page size (default: 256x256) - --pack-padding N: Padding between sprites (default: 1)

    Output: - Packed sprite sheet image - Individual sprite headers with offsets - Packing metadata

    Output Controls

    Endianness

    Specify byte order for multi-byte values:

    # Little-endian (default for most systems)
    -pr32-sprite-compiler sprite.png output.h --endian little
    -
    -# Big-endian
    -pr32-sprite-compiler sprite.png output.h --endian big
    -

    Alignment

    Control data structure alignment:

    pr32-sprite-compiler sprite.png output.h --align 4
    -

    Common values: - 1: No alignment (default) - 2: 2-byte alignment - 4: 4-byte alignment (recommended for ESP32) - 8: 8-byte alignment

    Output Format

    Choose output code style:

    # C-style (default)
    -pr32-sprite-compiler sprite.png output.h --style c
    -
    -# C++ style
    -pr32-sprite-compiler sprite.png output.h --style cpp
    -
    -# Minimal (no comments)
    -pr32-sprite-compiler sprite.png output.h --style minimal
    -

    Custom Header Template

    Use custom header template:

    pr32-sprite-compiler sprite.png output.h \
    -    --template custom_template.h
    -

    Template variables: - {NAME}: Sprite name - {DATA}: Sprite data array - {WIDTH}: Sprite width - {HEIGHT}: Sprite height

    Metadata Generation

    Generate Metadata

    Include sprite metadata:

    pr32-sprite-compiler sprite.png output.h --metadata
    -

    Output includes: - Original image dimensions - Conversion parameters - Color information - Format details

    JSON Metadata

    Export metadata as JSON:

    pr32-sprite-compiler sprite.png output.h \
    -    --metadata \
    -    --metadata-format json \
    -    --metadata-file sprite.json
    -

    JSON structure:

    {
    -  "name": "SPRITE",
    -  "width": 8,
    -  "height": 8,
    -  "format": "1bpp",
    -  "originalSize": {"width": 8, "height": 8},
    -  "colors": 2,
    -  "dataSize": 8
    -}
    -

    Color Processing

    Color Quantization

    Reduce colors intelligently:

    pr32-sprite-compiler sprite.png output.h \
    -    --quantize \
    -    --colors 4
    -

    Parameters: - --quantize: Enable quantization - --colors N: Target color count

    Color Mapping

    Custom color mapping:

    pr32-sprite-compiler sprite.png output.h \
    -    --color-map map.json
    -

    Map JSON format:

    {
    -  "mappings": [
    -    {"from": [255, 0, 0], "to": [255, 255, 255]},
    -    {"from": [0, 255, 0], "to": [0, 0, 0]}
    -  ]
    -}
    -

    Gamma Correction

    Apply gamma correction:

    pr32-sprite-compiler sprite.png output.h --gamma 2.2
    -

    Common values: - 1.0: No correction - 2.2: Standard (sRGB) - 1.8: Mac standard

    Performance Optimization

    Parallel Processing

    Process multiple files in parallel:

    pr32-sprite-compiler --batch sprites/*.png \
    -    --output-dir generated/ \
    -    --parallel 4
    -

    Parameters: - --parallel N: Number of parallel workers (default: CPU cores)

    Caching

    Enable conversion caching:

    pr32-sprite-compiler sprite.png output.h --cache
    -

    Benefits: - Faster re-compilation - Skips unchanged files - Cache stored in .sprite-compiler-cache/

    Incremental Builds

    Only process changed files:

    pr32-sprite-compiler --batch sprites/*.png \
    -    --output-dir generated/ \
    -    --incremental
    -

    Validation

    Validate Output

    Verify generated sprite data:

    pr32-sprite-compiler sprite.png output.h --validate
    -

    Checks: - Data array correctness - Size constraints - Format compliance - Memory usage

    Preview Mode

    Preview conversion without generating file:

    pr32-sprite-compiler sprite.png --preview
    -

    Output: - Console preview of sprite data - Statistics (size, colors, etc.) - Validation results

    Multi-Format Output

    Generate Multiple Formats

    Output in multiple formats simultaneously:

    pr32-sprite-compiler sprite.png output.h \
    -    --formats 1bpp,2bpp,4bpp \
    -    --output-dir formats/
    -

    Outputs: - output_1bpp.h - output_2bpp.h - output_4bpp.h

    Custom Scripts

    Pre-Processing Script

    Run custom script before conversion:

    pr32-sprite-compiler sprite.png output.h \
    -    --pre-script preprocess.js
    -

    Post-Processing Script

    Run custom script after conversion:

    pr32-sprite-compiler sprite.png output.h \
    -    --post-script postprocess.js
    -

    Script receives: - Input file path - Output file path - Conversion parameters - Metadata

    Advanced Batch Options

    Filter by Size

    Process only sprites matching size:

    pr32-sprite-compiler --batch sprites/*.png \
    -    --output-dir generated/ \
    -    --filter-size 8x8
    -

    Filter by Format

    Process only specific formats:

    pr32-sprite-compiler --batch sprites/*.png \
    -    --output-dir generated/ \
    -    --filter-format indexed
    -

    Exclude Patterns

    Exclude files matching pattern:

    pr32-sprite-compiler --batch sprites/*.png \
    -    --output-dir generated/ \
    -    --exclude "*_old.png"
    -

    Integration Examples

    Watch Mode

    Automatically recompile on file changes:

    pr32-sprite-compiler --watch assets/sprites/ \
    -    --output-dir src/sprites/
    -

    CI/CD Integration

    Example GitHub Actions workflow:

    name: Compile Sprites
    -on: [push]
    -jobs:
    -  compile:
    -    runs-on: ubuntu-latest
    -    steps:
    -      - uses: actions/checkout@v2
    -      - uses: actions/setup-node@v2
    -      - run: npm install -g pr32-sprite-compiler
    -      - run: pr32-sprite-compiler --batch assets/sprites/*.png --output-dir src/sprites/
    -

    Best Practices

    When to Use Advanced Features

    Dithering: - Use for gradients and smooth transitions - Avoid for sharp, pixel-art style graphics

    Layer Merging: - Use for complex sprites with multiple layers - Test performance impact first

    Packing: - Use for large sprite collections - Consider memory vs. performance trade-off

    Trimming: - Always use for sprites with transparent borders - Saves memory and improves performance

    Performance Tips

    • Use --parallel for batch processing
    • Enable --cache for development
    • Use --incremental for large projects
    • Validate output with --validate

    Troubleshooting Advanced Features

    Dithering Issues

    Too much noise: - Reduce --dither-strength - Try different algorithm - Consider manual color reduction

    Packing Issues

    Sprites don't fit: - Increase --pack-size - Reduce sprite sizes - Adjust --pack-padding

    Performance Issues

    Slow compilation: - Use --parallel for batch - Enable --cache - Reduce image sizes - Use --incremental

    See Also

    \ No newline at end of file + Advanced Features - PixelRoot32 Documentation

    Sprite Compiler Advanced Features

    Advanced features and options for the PixelRoot32 Sprite Compiler to optimize sprite conversion and handle complex scenarios.

    Automatic Palette Detection

    The Sprite Compiler automatically detects if your sprite uses one of the engine's built-in palettes. This simplifies your workflow and ensures color consistency.

    Predefined Engine Palettes

    The engine includes 5 optimized palettes: - PR32 (Default PixelRoot32 palette) - NES (Nintendo style) - GB (GameBoy style) - GBC (GameBoy Color style) - PICO8 (Fantasy console style)

    How it works

    1. Detection: When you compile an image, the tool compares all unique colors found in the sprite with the colors in the 5 engine palettes.
    2. Match: If all colors in your sprite belong to one of these palettes, the compiler:
    3. Omits generating a color array in the header.
    4. Assumes you will use the engine's built-in palette definitions at runtime.
    5. Custom Palette: If your sprite uses colors not found in the engine palettes, it automatically generates a {PREFIX}_PALETTE_MAPPING[16] array in the header file.

    Naming with Prefixes

    You can organize your generated code by using the --prefix parameter (or the Prefix field in the GUI).

    Using Prefixes

    By default, sprites are named SPRITE_N_.... Using a prefix allows you to create more descriptive names and avoid naming collisions.

    python main.py sheet.png --grid 16x16 --sprite 0,0,1,1 --prefix PLAYER_JUM
    +

    Generated names will follow this pattern: - PLAYER_JUM_SPRITE_0_LAYER_0 - PLAYER_JUM_SPRITE_0_2BPP - PLAYER_JUM_SPRITE_0_4BPP - PLAYER_JUM_PALETTE_MAPPING (if a custom palette is used)

    Export Modes

    Layered (1bpp)

    Best for standard PixelRoot32 rendering. It extracts each color into its own bitmask (1bpp). The engine then renders these layers sequentially.

    Packed (2bpp / 4bpp)

    Generates a single packed array where each pixel uses multiple bits. - 2bpp: 4 colors max (Index 0 is always transparent). - 4bpp: 16 colors max (Index 0 is always transparent).

    These modes are more efficient for sprites with many colors as they require only a single draw call.

    \ No newline at end of file diff --git a/site/tools/sprite_compiler/installation/index.html b/site/tools/sprite_compiler/installation/index.html index 2df7a91..982f241 100644 --- a/site/tools/sprite_compiler/installation/index.html +++ b/site/tools/sprite_compiler/installation/index.html @@ -1,38 +1,32 @@ - Installation - PixelRoot32 Documentation

    Sprite Compiler Installation

    This guide walks you through installing the PixelRoot32 Sprite Compiler on your system.

    Prerequisites

    Required Software

    • Node.js: Version 14.0 or higher
    • npm: Usually included with Node.js
    • Git: For cloning from source (optional)

    Verify Prerequisites

    Check if Node.js is installed:

    node --version
    -# Should show v14.0.0 or higher
    -

    Check if npm is installed:

    npm --version
    + Installation - PixelRoot32 Documentation      

    Sprite Compiler Installation

    This guide walks you through installing the PixelRoot32 Sprite Compiler on your system.

    Prerequisites

    Required Software

    • Python: Version 3.8 or higher
    • pip: Usually included with Python

    Verify Prerequisites

    Check if Python is installed:

    python --version
    +# Should show 3.8.0 or higher
    +

    Check if pip is installed:

    pip --version
     # Should show version number
    -

    If not installed, download from nodejs.org

    Installation Methods

    The easiest way to install the Sprite Compiler is via npm:

    npm install -g pr32-sprite-compiler
    -

    This installs the compiler globally, making it available from any directory.

    Verify installation:

    pr32-sprite-compiler --version
    -

    Method 2: From Source

    If you want the latest development version or need to customize:

    Step 1: Clone Repository

    git clone https://github.com/Gperez88/pr32-sprite-compiler.git
    -cd pr32-sprite-compiler
    -

    Step 2: Install Dependencies

    npm install
    -

    Step 3: Install Locally (Optional)

    To use the compiler from anywhere:

    npm link
    -

    This creates a global symlink to the local installation.

    Step 4: Verify Installation

    pr32-sprite-compiler --version
    -

    Method 3: Local Project Installation

    For project-specific installation (not global):

    cd your-pixelroot32-project
    -npm install pr32-sprite-compiler --save-dev
    -

    Then use via npx:

    npx pr32-sprite-compiler input.png output.h
    -

    Platform-Specific Instructions

    Windows

    1. Install Node.js from nodejs.org
    2. Download the Windows installer
    3. Run the installer
    4. Restart your terminal/command prompt

    5. Open Command Prompt or PowerShell

    6. Install globally:

      npm install -g pr32-sprite-compiler
      -

    7. Verify:

      pr32-sprite-compiler --version
      -

    Troubleshooting Windows Issues

    "pr32-sprite-compiler is not recognized": - Ensure Node.js is in your PATH - Restart terminal after installation - Try using full path: C:\Users\YourName\AppData\Roaming\npm\pr32-sprite-compiler.cmd

    Permission errors: - Run terminal as Administrator - Or install locally: npm install pr32-sprite-compiler (without -g)

    Linux

    Using npm

    1. Install Node.js (if not already installed):

    Ubuntu/Debian:

    curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
    -sudo apt-get install -y nodejs
    -

    Fedora/RHEL:

    sudo dnf install nodejs npm
    -

    1. Install Sprite Compiler:

      sudo npm install -g pr32-sprite-compiler
      -

    2. Verify:

      pr32-sprite-compiler --version
      -

    Troubleshooting Linux Issues

    Permission denied: - Use sudo for global installation - Or install locally without -g flag

    Command not found: - Check npm global bin path: npm config get prefix - Add to PATH if needed: export PATH=$PATH:$(npm config get prefix)/bin

    macOS

    Using npm

    1. Install Node.js:
    2. Download from nodejs.org
    3. Or use Homebrew: brew install node

    4. Install Sprite Compiler:

      npm install -g pr32-sprite-compiler
      -

    5. Verify:

      pr32-sprite-compiler --version
      -

    Using Homebrew (Alternative)

    If available as a Homebrew formula:

    brew install pr32-sprite-compiler
    -

    GUI Version Installation

    If a GUI version is available:

    Windows

    Download the installer from the releases page and run it.

    Linux

    # Download AppImage or .deb package
    -# Make executable and run
    -chmod +x pr32-sprite-compiler-gui.AppImage
    -./pr32-sprite-compiler-gui.AppImage
    -

    macOS

    Download the .dmg file from releases and install.

    Verification

    After installation, verify everything works:

    Test Basic Functionality

    1. Create a test image:
    2. Create an 8x8 pixel PNG image (black and white)
    3. Save as test.png

    4. Run compiler:

      pr32-sprite-compiler test.png test_output.h
      -

    5. Check output:

    6. File test_output.h should be created
    7. Should contain sprite data arrays

    Check Version

    pr32-sprite-compiler --version
    -

    Check Help

    pr32-sprite-compiler --help
    -

    Updating

    Update via npm

    npm update -g pr32-sprite-compiler
    -

    Update from Source

    cd pr32-sprite-compiler
    -git pull
    -npm install
    -

    Uninstallation

    Remove Global Installation

    npm uninstall -g pr32-sprite-compiler
    -

    Remove Local Installation

    npm uninstall pr32-sprite-compiler
    -

    Troubleshooting

    Common Issues

    "Command not found" after installation: - Restart your terminal - Check npm global bin path: npm config get prefix - Verify PATH includes npm bin directory

    Permission errors: - On Linux/macOS: Use sudo for global install - Or install locally without -g flag - On Windows: Run terminal as Administrator

    Module not found errors: - Reinstall: npm install -g pr32-sprite-compiler - Clear npm cache: npm cache clean --force

    Version conflicts: - Check Node.js version: node --version - Update Node.js if version is too old - Use nvm (Node Version Manager) to manage versions

    Getting Help

    Next Steps

    Once installed, proceed to: - Usage Guide - Learn how to use the compiler - Advanced Features - Explore advanced options

    See Also

    \ No newline at end of file +

    If not installed, download from python.org

    Installation Methods

    Step 1: Clone Repository

    git clone https://github.com/Gperez88/PixelRoot32-Sprite-Compiler.git
    +cd PixelRoot32-Sprite-Compiler
    +

    Step 2: Install Dependencies

    pip install -r requirements.txt
    +

    Step 3: Verify Installation

    python main.py --help
    +

    Method 2: Standalone Executable (Windows)

    If you are on Windows, you can download the latest standalone .exe from the Releases section of the repository. This does not require Python or any dependencies to be installed.

    Platform-Specific Instructions

    Windows

    1. Install Node.js from nodejs.org
    2. Download the Windows installer
    3. Run the installer
    4. Restart your terminal/command prompt

    5. Open Command Prompt or PowerShell

    6. Install globally:

      npm install -g pr32-sprite-compiler
      +

    7. Verify:

      pr32-sprite-compiler --version
      +

    Troubleshooting Windows Issues

    "pr32-sprite-compiler is not recognized": - Ensure Node.js is in your PATH - Restart terminal after installation - Try using full path: C:\Users\YourName\AppData\Roaming\npm\pr32-sprite-compiler.cmd

    Permission errors: - Run terminal as Administrator - Or install locally: npm install pr32-sprite-compiler (without -g)

    Linux

    Using npm

    1. Install Node.js (if not already installed):

    Ubuntu/Debian:

    curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
    +sudo apt-get install -y nodejs
    +

    Fedora/RHEL:

    sudo dnf install nodejs npm
    +

    1. Install Sprite Compiler:

      sudo npm install -g pr32-sprite-compiler
      +

    2. Verify:

      pr32-sprite-compiler --version
      +

    Troubleshooting Linux Issues

    Permission denied: - Use sudo for global installation - Or install locally without -g flag

    Command not found: - Check npm global bin path: npm config get prefix - Add to PATH if needed: export PATH=$PATH:$(npm config get prefix)/bin

    macOS

    Using npm

    1. Install Node.js:
    2. Download from nodejs.org
    3. Or use Homebrew: brew install node

    4. Install Sprite Compiler:

      npm install -g pr32-sprite-compiler
      +

    5. Verify:

      pr32-sprite-compiler --version
      +

    Using Homebrew (Alternative)

    If available as a Homebrew formula:

    brew install pr32-sprite-compiler
    +

    GUI Version Installation

    If a GUI version is available:

    Windows

    Download the installer from the releases page and run it.

    Linux

    # Download AppImage or .deb package
    +# Make executable and run
    +chmod +x pr32-sprite-compiler-gui.AppImage
    +./pr32-sprite-compiler-gui.AppImage
    +

    macOS

    Download the .dmg file from releases and install.

    Verification

    After installation, verify everything works:

    Test Basic Functionality

    1. Create a test image:
    2. Create an 8x8 pixel PNG image (black and white)
    3. Save as test.png

    4. Run compiler:

      pr32-sprite-compiler test.png test_output.h
      +

    5. Check output:

    6. File test_output.h should be created
    7. Should contain sprite data arrays

    Check Version

    pr32-sprite-compiler --version
    +

    Check Help

    pr32-sprite-compiler --help
    +

    Updating

    Update via npm

    npm update -g pr32-sprite-compiler
    +

    Update from Source

    cd pr32-sprite-compiler
    +git pull
    +npm install
    +

    Uninstallation

    Remove Global Installation

    npm uninstall -g pr32-sprite-compiler
    +

    Remove Local Installation

    npm uninstall pr32-sprite-compiler
    +

    Troubleshooting

    Common Issues

    "Command not found" after installation: - Restart your terminal - Check npm global bin path: npm config get prefix - Verify PATH includes npm bin directory

    Permission errors: - On Linux/macOS: Use sudo for global install - Or install locally without -g flag - On Windows: Run terminal as Administrator

    Module not found errors: - Reinstall: npm install -g pr32-sprite-compiler - Clear npm cache: npm cache clean --force

    Version conflicts: - Check Node.js version: node --version - Update Node.js if version is too old - Use nvm (Node Version Manager) to manage versions

    Getting Help

    Next Steps

    Once installed, proceed to: - Usage Guide - Learn how to use the compiler - Advanced Features - Explore advanced options

    See Also

    \ No newline at end of file diff --git a/site/tools/sprite_compiler/overview/index.html b/site/tools/sprite_compiler/overview/index.html index 6f1e599..357577c 100644 --- a/site/tools/sprite_compiler/overview/index.html +++ b/site/tools/sprite_compiler/overview/index.html @@ -1,38 +1,27 @@ - Overview - PixelRoot32 Documentation

    Sprite Compiler Overview

    The Sprite Compiler (pr32-sprite-compiler) is a command-line tool that converts PNG images into PixelRoot32 sprite data formats. It automates the process of creating sprite arrays from image files, saving you time and ensuring correct format conversion.

    What It Does

    The Sprite Compiler takes bitmap images (PNG) and converts them into C/C++ header files containing:

    • Sprite data arrays: Optimized uint16_t arrays for 1bpp sprites
    • Sprite structures: Ready-to-use pixelroot32::graphics::Sprite definitions
    • Multi-sprite support: Handles sprite sheets and animations
    • Format options: Supports 1bpp (standard), 2bpp, and 4bpp formats

    Key Features

    ✅ Format Support

    • 1bpp (Monochrome): Standard format, most memory-efficient
    • 2bpp (4 colors): Experimental, requires build flags
    • 4bpp (16 colors): Experimental, requires build flags
    • MultiSprite: Layer compositions for complex sprites

    ✅ Batch Processing

    Process multiple images at once:

    pr32-sprite-compiler --batch sprites/*.png --output-dir generated/
    -

    ✅ Sprite Sheets

    Automatically split sprite sheets into individual sprites:

    pr32-sprite-compiler sheet.png output.h --sheet 8x8 --count 16
    -

    ✅ Custom Palettes

    Use custom color palettes for conversion:

    pr32-sprite-compiler input.png output.h --palette custom.json
    -

    ✅ GUI Version (Optional)

    Visual interface for: - Drag and drop image processing - Real-time preview of sprite data - Visual settings adjustment - Export to header files

    Input Requirements

    Supported Formats

    • PNG: Primary format (recommended)
    • Indexed color PNG: Best for 1bpp conversion
    • Grayscale PNG: Automatically converted to 1bpp
    • RGB PNG: Converted using threshold or palette

    Image Constraints

    For 1bpp sprites: - Maximum width: 16 pixels - Height: Any (typically 8, 16, 32 pixels) - Colors: Black and white (or converted automatically)

    For 2bpp sprites: - Maximum width: 16 pixels - Colors: Up to 4 colors

    For 4bpp sprites: - Maximum width: 16 pixels - Colors: Up to 16 colors

    Output Format

    The compiler generates C++ header files with:

    #ifndef SPRITE_DATA_H
    -#define SPRITE_DATA_H
    -
    -#include <stdint.h>
    -#include <graphics/Sprite.h>
    -
    -// Sprite data array
    -static const uint16_t MY_SPRITE_DATA[] = {
    -    0b00111100,  // Row 0
    -    0b01111110,  // Row 1
    -    0b11111111,  // Row 2
    -    // ... more rows
    -};
    -
    -// Sprite structure
    -static const pixelroot32::graphics::Sprite MY_SPRITE = {
    -    MY_SPRITE_DATA,  // data pointer
    -    8,                // width
    -    8                 // height
    -};
    -
    -#endif
    -

    Use Cases

    1. Single Sprite Conversion

    Convert a single image to a sprite:

    pr32-sprite-compiler player.png player_sprite.h --name PLAYER_SPRITE
    -

    2. Animation Frames

    Convert multiple frames for animation:

    pr32-sprite-compiler --batch walk_*.png --output-dir animations/ --prefix WALK_
    -

    3. Sprite Sheet Processing

    Split a sprite sheet into individual sprites:

    pr32-sprite-compiler characters.png output.h --sheet 16x16 --count 8
    -

    4. Batch Asset Processing

    Process entire asset directories:

    pr32-sprite-compiler --batch assets/sprites/*.png --output-dir src/sprites/
    -

    Workflow Integration

    Typical Development Workflow

    1. Create sprites in your image editor (Aseprite, Piskel, GIMP, etc.)
    2. Save as PNG with appropriate dimensions
    3. Run compiler to generate header files
    4. Include headers in your PixelRoot32 project
    5. Use sprites in your game code

    Automation Example

    #!/bin/bash
    -# build-sprites.sh
    -
    -# Compile all sprites
    -pr32-sprite-compiler assets/sprites/*.png --output-dir src/sprites/
    -
    -# Continue with your build process
    -platformio run
    -

    Advantages Over Manual Creation

    ✅ Time Saving

    • No manual bit pattern conversion
    • Automatic format optimization
    • Batch processing multiple sprites

    ✅ Accuracy

    • Correct bit ordering
    • Proper array formatting
    • Valid C++ syntax

    ✅ Consistency

    • Uniform naming conventions
    • Standardized output format
    • Consistent code structure

    ✅ Maintainability

    • Easy to regenerate from source images
    • Version control friendly
    • Clear separation of assets and code

    Limitations

    • Width limit: 16 pixels for 1bpp (hardware constraint)
    • Color depth: Limited by format (1bpp = 2 colors, 2bpp = 4 colors, etc.)
    • File format: Primarily PNG (other formats may require conversion)

    Next Steps

    See Also

    \ No newline at end of file + Overview - PixelRoot32 Documentation

    Sprite Compiler Overview

    The Sprite Compiler is a tool that converts PNG images into PixelRoot32 sprite data formats. It provides both a graphical interface (GUI) and a command-line interface (CLI) to automate the process of creating sprite arrays from image files.

    What It Does

    The Sprite Compiler takes bitmap images (PNG) and converts them into C header files containing:

    • Sprite data arrays: Optimized uint16_t arrays for various formats.
    • Layered support: Generates multiple 1bpp layers for complex sprites.
    • Packed formats: Supports 2bpp and 4bpp packed formats.
    • Sprite sheets: Handles grid-based sprite sheets with auto-detection.

    Key Features

    ✅ Format Support

    • Layered (1bpp): Standard format, generates one array per color.
    • 2bpp (4 colors): Packed format, 2 bits per pixel.
    • 4bpp (16 colors): Packed format, 4 bits per pixel.

    ✅ GUI & CLI

    • Modern GUI: Step-by-step card-based interface for easy configuration.
    • Powerful CLI: Perfect for build scripts and automation.

    ✅ Sprite Sheets

    Automatically split sprite sheets into individual sprites:

    python main.py sheet.png --grid 16x16 --sprite 0,0,1,1 --sprite 1,0,1,1 --out output.h
    +

    GUI Interface

    The GUI is designed to be intuitive and follows a 5-step process:

    1. Input Image: Select your PNG source.
    2. Grid Settings: Define the cell size and offsets.
    3. Sprite Selection: Pick which cells to export.
    4. Export Settings: Choose the mode (Layered, 2bpp, 4bpp), set a Prefix, and choose the output path.
    5. About: Quick access to version info and credits (via the ? button).
    6. Log: Technical feedback and performance alerts.

    Input Requirements

    Supported Formats

    • PNG: Primary format (recommended)
    • Indexed color PNG: Best for 1bpp conversion
    • Grayscale PNG: Automatically converted to 1bpp
    • RGB PNG: Converted using threshold or palette

    Image Constraints

    For 1bpp sprites: - Maximum width: 16 pixels - Height: Any (typically 8, 16, 32 pixels) - Colors: Black and white (or converted automatically)

    For 2bpp sprites: - Maximum width: 16 pixels - Colors: Up to 4 colors

    For 4bpp sprites: - Maximum width: 16 pixels - Colors: Up to 16 colors

    Output Format

    The compiler generates C header files with optimized arrays:

    // Generated by PixelRoot32 Sprite Compiler
    +
    +// Optional palette mapping if using custom colors
    +static const Color PLAYER_PALETTE_MAPPING[16] = {
    +    (Color)0, (Color)1, (Color)2, (Color)3,
    +    // ...
    +};
    +
    +// Sprite data array (4bpp example)
    +static const uint16_t PLAYER_SPRITE_0_4BPP[] = {
    +    0x0000, 0x1234, 0x5678, // Row 0
    +    // ... more rows
    +};
    +

    Use Cases

    1. Single Sprite Conversion

    Convert a single image to a sprite:

    pr32-sprite-compiler player.png player_sprite.h --name PLAYER_SPRITE
    +

    2. Animation Frames

    Convert multiple frames for animation:

    pr32-sprite-compiler --batch walk_*.png --output-dir animations/ --prefix WALK_
    +

    3. Sprite Sheet Processing

    Split a sprite sheet into individual sprites:

    pr32-sprite-compiler characters.png output.h --sheet 16x16 --count 8
    +

    4. Batch Asset Processing

    Process entire asset directories:

    pr32-sprite-compiler --batch assets/sprites/*.png --output-dir src/sprites/
    +

    Workflow Integration

    Typical Development Workflow

    1. Create sprites in your image editor (Aseprite, Piskel, GIMP, etc.)
    2. Save as PNG with appropriate dimensions
    3. Run compiler to generate header files
    4. Include headers in your PixelRoot32 project
    5. Use sprites in your game code

    Automation Example

    #!/bin/bash
    +# build-sprites.sh
    +
    +# Compile all sprites
    +pr32-sprite-compiler assets/sprites/*.png --output-dir src/sprites/
    +
    +# Continue with your build process
    +platformio run
    +

    Advantages Over Manual Creation

    ✅ Time Saving

    • No manual bit pattern conversion
    • Automatic format optimization
    • Batch processing multiple sprites

    ✅ Accuracy

    • Correct bit ordering
    • Proper array formatting
    • Valid C++ syntax

    ✅ Consistency

    • Uniform naming conventions
    • Standardized output format
    • Consistent code structure

    ✅ Maintainability

    • Easy to regenerate from source images
    • Version control friendly
    • Clear separation of assets and code

    Limitations

    • Width limit: 16 pixels for 1bpp (hardware constraint)
    • Color depth: Limited by format (1bpp = 2 colors, 2bpp = 4 colors, etc.)
    • File format: Primarily PNG (other formats may require conversion)

    Next Steps

    See Also

    \ No newline at end of file diff --git a/site/tools/sprite_compiler/usage_guide/index.html b/site/tools/sprite_compiler/usage_guide/index.html index 16630b5..0ddae19 100644 --- a/site/tools/sprite_compiler/usage_guide/index.html +++ b/site/tools/sprite_compiler/usage_guide/index.html @@ -1,17 +1,22 @@ - Usage Guide - PixelRoot32 Documentation

    Sprite Compiler Usage Guide

    Complete guide to using the PixelRoot32 Sprite Compiler for converting images to sprite data.

    Basic Usage

    Simple Conversion

    Convert a single PNG image to a sprite header file:

    pr32-sprite-compiler input.png output.h
    -

    This creates output.h with sprite data from input.png.

    With Custom Name

    Specify a custom name for the sprite:

    pr32-sprite-compiler player.png player_sprite.h --name PLAYER_SPRITE
    -

    The generated sprite will be named PLAYER_SPRITE instead of the default.

    Command Line Options

    Basic Options

    pr32-sprite-compiler [input] [output] [options]
    -

    Required: - input: Input PNG image file - output: Output header file path

    Common Options: - --name NAME: Custom sprite name (default: derived from filename) - --format FORMAT: Output format (1bpp, 2bpp, 4bpp) - --width W: Force sprite width (pixels) - --height H: Force sprite height (pixels)

    Format Selection

    1bpp (Monochrome) - Default:

    pr32-sprite-compiler sprite.png output.h --format 1bpp
    -

    2bpp (4 colors) - Experimental:

    pr32-sprite-compiler sprite.png output.h --format 2bpp
    -

    4bpp (16 colors) - Experimental:

    pr32-sprite-compiler sprite.png output.h --format 4bpp
    -

    Step-by-Step Examples

    Example 1: Simple Player Sprite

    Step 1: Create Image - Create an 8x8 pixel PNG image - Use black and white colors - Save as player.png

    Step 2: Compile

    pr32-sprite-compiler player.png player_sprite.h --name PLAYER_SPRITE
    -

    Step 3: Use in Code

    #include "player_sprite.h"
    + Usage Guide - PixelRoot32 Documentation      

    Sprite Compiler Usage Guide

    Complete guide to using the PixelRoot32 Sprite Compiler for converting images to sprite data.

    Basic Usage

    Launching the GUI

    The easiest way to use the compiler is via the Graphical User Interface (GUI).

    python main.py
    +

    This will open the application where you can interactively load images, configure the grid, and export sprites.

    Command Line Interface (CLI)

    For automation, you can use the CLI mode by passing arguments to the script.

    python main.py [input] [options]
    +

    Required:

    • input: Input PNG image file
    • --grid WxH: Grid cell size (e.g., 16x16)
    • --sprite gx,gy,gw,gh: Sprite definition (can be repeated)

    Optional:

    • --prefix NAME: Prefix for generated arrays (e.g., PLAYER_JUM)
    • --out FILE: Output header file (default: sprites.h)
    • --offset X,Y: Initial offset in pixels (default: 0,0)
    • --mode MODE: Export mode (layered, 2bpp, 4bpp)

    CLI Examples

    Simple Conversion

    Convert a single 16x16 sprite located at the top-left corner:

    python main.py player.png --grid 16x16 --sprite 0,0,1,1 --out player.h
    +

    Multiple Sprites

    Convert multiple sprites from a single sheet:

    python main.py sheet.png --grid 16x16 \
    +  --sprite 0,0,1,1 \
    +  --sprite 1,0,1,1 \
    +  --sprite 2,0,1,1 \
    +  --out animations.h
    +

    Export Modes

    Layered (Default): Generates multiple uint16_t arrays, one for each color layer. Best for standard PixelRoot32 rendering.

    python main.py icon.png --grid 16x16 --sprite 0,0,1,1 --mode layered
    +

    Packed 2bpp: Generates a single array with 2 bits per pixel (4 colors max).

    python main.py icon.png --grid 16x16 --sprite 0,0,1,1 --mode 2bpp
    +

    Step-by-Step Examples

    Example 1: Simple Player Sprite

    Step 1: Create Image

    • Create an 8x8 pixel PNG image
    • Use black and white colors
    • Save as player.png

    Step 2: Compile

    python main.py player.png --grid 8x8 --sprite 0,0,1,1 --prefix PLAYER --out player_sprite.h
    +

    Step 3: Use in Code

    #include "player_sprite.h"
     
     void draw() {
    -    renderer.drawSprite(PLAYER_SPRITE, 100, 100, Color::White);
    -}
    -

    Example 2: Multiple Animation Frames

    Step 1: Prepare Images - Create frames: walk_0.png, walk_1.png, walk_2.png - All same size (e.g., 16x16)

    Step 2: Batch Compile

    pr32-sprite-compiler --batch walk_*.png --output-dir animations/ --prefix WALK_
    -

    Step 3: Use in Animation

    #include "animations/walk_0.h"
    +    // PLAYER_SPRITE_0_LAYER_0 is generated automatically
    +    renderer.drawSprite(PLAYER_SPRITE_0_LAYER_0, 100, 100, Color::White);
    +}
    +

    Example 2: Multiple Animation Frames

    Step 1: Prepare Images

    • Create frames: walk_0.png, walk_1.png, walk_2.png
    • All same size (e.g., 16x16)

    Step 2: Batch Compile

    pr32-sprite-compiler --batch walk_*.png --output-dir animations/ --prefix WALK_
    +

    Step 3: Use in Animation

    #include "animations/walk_0.h"
     #include "animations/walk_1.h"
     #include "animations/walk_2.h"
     
    @@ -20,36 +25,36 @@
         &WALK_1_SPRITE,
         &WALK_2_SPRITE
     };
    -

    Example 3: Sprite Sheet

    Step 1: Create Sprite Sheet - Create a 64x64 image with 4x4 grid of 16x16 sprites - Save as characters.png

    Step 2: Split Sheet

    pr32-sprite-compiler characters.png characters.h --sheet 16x16 --count 16
    -

    Step 3: Use Individual Sprites

    #include "characters.h"
    +

    Example 3: Sprite Sheet

    Step 1: Create Sprite Sheet

    • Create a 64x64 image with 4x4 grid of 16x16 sprites
    • Save as characters.png

    Step 2: Split Sheet

    pr32-sprite-compiler characters.png characters.h --sheet 16x16 --count 16
    +

    Step 3: Use Individual Sprites

    #include "characters.h"
     
     // Sprites named CHARACTER_0, CHARACTER_1, etc.
     renderer.drawSprite(CHARACTER_0, 50, 50, Color::White);
     renderer.drawSprite(CHARACTER_1, 70, 50, Color::White);
    -

    Batch Processing

    Process Multiple Files

    Process all PNG files in a directory:

    pr32-sprite-compiler --batch sprites/*.png --output-dir generated/
    +

    Batch Processing

    Process Multiple Files

    Process all PNG files in a directory:

    pr32-sprite-compiler --batch sprites/*.png --output-dir generated/
     

    With Options

    Apply options to all files:

    pr32-sprite-compiler --batch assets/*.png \
         --output-dir src/sprites/ \
         --format 1bpp \
         --prefix SPRITE_
     

    Recursive Processing

    Process subdirectories:

    pr32-sprite-compiler --batch assets/**/*.png --output-dir generated/
     

    Sprite Sheets

    Automatic Splitting

    Split a sprite sheet into individual sprites:

    pr32-sprite-compiler sheet.png output.h --sheet 8x8 --count 16
    -

    Parameters: - --sheet WxH: Tile size (width x height) - --count N: Number of sprites in sheet

    Grid Layout

    Specify grid dimensions:

    pr32-sprite-compiler sheet.png output.h \
    +

    Parameters:

    • --sheet WxH: Tile size (width x height)
    • --count N: Number of sprites in sheet

    Grid Layout

    Specify grid dimensions:

    pr32-sprite-compiler sheet.png output.h \
         --sheet 16x16 \
         --grid 4x4 \
         --count 16
    -

    Parameters: - --grid WxH: Grid dimensions (columns x rows)

    Custom Naming

    Name sprites with index:

    pr32-sprite-compiler sheet.png output.h \
    +

    Parameters:

    • --grid WxH: Grid dimensions (columns x rows)

    Custom Naming

    Name sprites with index:

    pr32-sprite-compiler sheet.png output.h \
         --sheet 8x8 \
         --count 8 \
         --prefix CHARACTER_ \
         --indexed
     

    Generates: CHARACTER_0, CHARACTER_1, etc.

    Custom Palettes

    Using Palette File

    Convert with custom color palette:

    pr32-sprite-compiler sprite.png output.h --palette palette.json
    -

    Palette JSON format:

    {
    +

    Palette JSON format:

    {
       "colors": [
         {"r": 0, "g": 0, "b": 0, "name": "black"},
         {"r": 255, "g": 255, "b": 255, "name": "white"}
       ]
     }
    -

    Built-in Palettes

    Use predefined palettes:

    pr32-sprite-compiler sprite.png output.h --palette nes
    +

    Built-in Palettes

    Use predefined palettes:

    pr32-sprite-compiler sprite.png output.h --palette nes
     pr32-sprite-compiler sprite.png output.h --palette gb
     pr32-sprite-compiler sprite.png output.h --palette pico8
     

    Advanced Options

    Threshold for Grayscale

    Set threshold for black/white conversion:

    pr32-sprite-compiler sprite.png output.h --threshold 128
    @@ -65,7 +70,7 @@
     

    Integration with Build Systems

    PlatformIO

    Add to platformio.ini:

    [env:esp32dev]
     extra_scripts = 
         pre:scripts/compile_sprites.py
    -

    compile_sprites.py:

    Import("env")
    +

    compile_sprites.py:

    Import("env")
     import subprocess
     
     subprocess.run([
    @@ -73,11 +78,11 @@
         "--batch", "assets/sprites/*.png",
         "--output-dir", "src/sprites/"
     ])
    -

    Makefile

    SPRITES = $(wildcard assets/sprites/*.png)
    +

    Makefile

    SPRITES = $(wildcard assets/sprites/*.png)
     SPRITE_HEADERS = $(SPRITES:assets/sprites/%.png=src/sprites/%.h)
     
     src/sprites/%.h: assets/sprites/%.png
    -    pr32-sprite-compiler $< $@ --name $(shell basename $< .png | tr '[:lower:]' '[:upper:]')_SPRITE
    + pr32-sprite-compiler $< $@ --name $(shell basename $< .png | tr '[:lower:]' '[:upper:]')_SPRITE
     
     sprites: $(SPRITE_HEADERS)
     

    CMake

    file(GLOB SPRITE_FILES "assets/sprites/*.png")
    @@ -104,5 +109,5 @@
     │       ├── enemy.h
     │       └── items.h
     └── platformio.ini
    -

    Naming Conventions

    • Use descriptive names: player_walk_0.pngPLAYER_WALK_0_SPRITE
    • Be consistent: All caps for sprite names
    • Use prefixes: ENEMY_, PLAYER_, ITEM_

    Version Control

    • Commit generated headers (they're part of the build)
    • Or add to .gitignore and regenerate on build
    • Keep source images in version control

    Troubleshooting

    Common Issues

    "Image too large": - Sprites must be ≤ 16 pixels wide for 1bpp - Resize image or split into multiple sprites

    "Colors not converting correctly": - Use indexed color PNG - For 1bpp: Use only black and white - For 2bpp: Use exactly 4 colors - For 4bpp: Use up to 16 colors

    "Output file not found": - Check write permissions - Verify output directory exists - Use absolute paths if needed

    "Invalid format": - Ensure input is PNG format - Check file is not corrupted - Try re-saving image in image editor

    Getting Help

    pr32-sprite-compiler --help
    -

    Shows all available options and usage.

    Next Steps

    See Also

    \ No newline at end of file +

    Naming Conventions

    • Use descriptive names: player_walk_0.pngPLAYER_WALK_0_SPRITE
    • Be consistent: All caps for sprite names
    • Use prefixes: ENEMY_, PLAYER_, ITEM_

    Version Control

    • Commit generated headers (they're part of the build)
    • Or add to .gitignore and regenerate on build
    • Keep source images in version control

    Troubleshooting

    Common Issues

    "Image too large":

    • Sprites must be ≤ 16 pixels wide for 1bpp
    • Resize image or split into multiple sprites

    "Colors not converting correctly":

    • Use indexed color PNG
    • For 1bpp: Use only black and white
    • For 2bpp: Use exactly 4 colors
    • For 4bpp: Use up to 16 colors

    "Output file not found":

    • Check write permissions
    • Verify output directory exists
    • Use absolute paths if needed

    "Invalid format":

    • Ensure input is PNG format
    • Check file is not corrupted
    • Try re-saving image in image editor

    Getting Help

    pr32-sprite-compiler --help
    +

    Shows all available options and usage.

    Next Steps

    See Also

    \ No newline at end of file diff --git a/site/tools/tilemap_editor/installation/index.html b/site/tools/tilemap_editor/installation/index.html index abe31ff..9a166ba 100644 --- a/site/tools/tilemap_editor/installation/index.html +++ b/site/tools/tilemap_editor/installation/index.html @@ -1,7 +1,7 @@ - Installation - PixelRoot32 Documentation

    Installation Guide

    The PixelRoot32 Tilemap Editor can be run directly from source or as a standalone executable on Windows.

    1. Requirements

    • Python 3.13+ (if running from source).
    • Windows 10/11 (recommended).

    2. Install from Source

    2.1 Clone the Repository

    git clone https://github.com/Gperez88/PixelRoot32-Tilemap-Editor.git
    + Installation Guide - PixelRoot32 Documentation      

    Installation Guide

    The PixelRoot32 Tilemap Editor can be run directly from source or as a standalone executable on Windows.

    1. Requirements

    • Python 3.13+ (if running from source).
    • Windows 10/11 (recommended).

    2. Install from Source

    2.1 Clone the Repository

    git clone https://github.com/Gperez88/PixelRoot32-Tilemap-Editor.git
     cd PixelRoot32-Tilemap-Editor
     

    2.2 Install Dependencies

    The editor uses several Python libraries for the GUI and image processing:

    pip install ttkbootstrap pillow jinja2
     

    2.3 Run the Editor

    python main.py
    -

    3. Standalone Executable (Windows)

    For a more convenient experience, you can use the pre-compiled version:

    1. Go to the Releases section of the repository.
    2. Download the latest PixelRoot32-Editor-win64.zip.
    3. Extract the contents to a folder.
    4. Run PixelRoot32-Editor.exe.

    Note: No Python installation is required to run the standalone executable.

    4. Building your own Executable

    If you want to package the editor yourself:

    1. Install PyInstaller:
      pip install pyinstaller
      -
    2. Run the build command using the provided .spec file:
      pyinstaller pixelroot32_editor.spec
      -
    3. The executable will be available in the dist/ folder.
    \ No newline at end of file +

    3. Standalone Executable (Windows)

    For a more convenient experience, you can use the pre-compiled version:

    1. Go to the Releases section of the repository.
    2. Download the latest PixelRoot32-Editor-win64.zip.
    3. Extract the contents to a folder.
    4. Run PixelRoot32-Editor.exe.

    Note: No Python installation is required to run the standalone executable.

    4. Building your own Executable

    If you want to package the editor yourself:

    1. Install PyInstaller:
    pip install pyinstaller
    +
    1. Run the build command using the provided .spec file:
    pyinstaller pixelroot32_editor.spec
    +
    1. The executable will be available in the dist/ folder.
    \ No newline at end of file diff --git a/site/tools/tilemap_editor/overview/index.html b/site/tools/tilemap_editor/overview/index.html index 8fee73a..c5f0c2a 100644 --- a/site/tools/tilemap_editor/overview/index.html +++ b/site/tools/tilemap_editor/overview/index.html @@ -1 +1 @@ - Overview - PixelRoot32 Documentation

    Tilemap Editor Overview

    The PixelRoot32 Tilemap Editor is a powerful visual tool designed to create complex multi-layered tile-based maps for the PixelRoot32 engine. It simplifies the process of designing game environments, managing tilesets, and exporting optimized C++ code.

    What It Does

    The Tilemap Editor allows you to:

    • Visual Design: Paint tiles directly onto a canvas with layers and transparency.
    • Tileset Management: Import PNG images as tilesets and select single or multiple tiles.
    • Multi-Layer Support: Organize your map into up to 8 layers for parallax effects or depth.
    • Optimized Export: Generate C++ header and source files compatible with the PixelRoot32 renderer.
    • BPP Support: Export maps in 1bpp, 2bpp, or 4bpp formats to balance memory usage and color depth.

    Key Features

    ✅ Visual Editing Tools

    • Brush: Paint individual tiles or patterns.
    • Eraser: Remove tiles from the active layer.
    • Rectangle Fill: Quickly fill areas with a specific tile.
    • Pipette: Pick an existing tile from the canvas.

    ✅ Multi-Layer System

    • Visibility Toggle: Hide/show layers to focus on specific parts of the map.
    • Opacity Control: Adjust layer transparency for complex blending effects.
    • Layer Reordering: Change the render order of your tilemaps.

    ✅ Tileset Selector

    • Smart Selection: Drag and select a rectangular area of tiles.
    • Multiple Tilesets: Support for multiple tilesets per project (planned).
    • Auto-import: Automatically detects tile size from the imported image.

    ✅ Engine Integration

    • Workspace Selection: Link the editor to your PixelRoot32 projects directory.
    • Direct Export: Files are generated with the correct namespaces and structures for immediate use.
    • BPP Compatibility: Ensures exported data matches the engine's expected format for 1bpp, 2bpp, and 4bpp.

    Data Formats

    Project File (.pr32scene)

    The editor uses a custom JSON-based format to save your project state, including: - Tileset metadata (path, tile size, spacing). - Layer data (tile indices, width, height, position). - Project settings (BPP, namespace).

    Exported C++

    The editor generates .h and .cpp files containing: - Tilemap Data: Packed arrays of tile indices. - Tilemap Structures: pixelroot32::graphics::TileMap (or TileMap2bpp/TileMap4bpp) definitions. - Scene Headers: Convenient entry points for loading entire maps into your game.

    \ No newline at end of file + Tilemap Editor Overview - PixelRoot32 Documentation

    Tilemap Editor Overview

    The PixelRoot32 Tilemap Editor is a powerful visual tool designed to create complex multi-layered tile-based maps for the PixelRoot32 engine. It simplifies the process of designing game environments, managing tilesets, and exporting optimized C++ code.

    What It Does

    The Tilemap Editor allows you to:

    • Visual Design: Paint tiles directly onto a canvas with layers and transparency.
    • Tileset Management: Import PNG images as tilesets and select single or multiple tiles.
    • Multi-Layer Support: Organize your map into up to 8 layers for parallax effects or depth.
    • Optimized Export: Generate C++ header and source files compatible with the PixelRoot32 renderer.
    • BPP Support: Export maps in 1bpp, 2bpp, or 4bpp formats to balance memory usage and color depth.

    Key Features

    ✅ Visual Editing Tools

    • Brush: Paint individual tiles or patterns.
    • Eraser: Remove tiles from the active layer.
    • Rectangle Fill: Quickly fill areas with a specific tile.
    • Pipette: Pick an existing tile from the canvas.

    ✅ Multi-Layer System

    • Visibility Toggle: Hide/show layers to focus on specific parts of the map.
    • Opacity Control: Adjust layer transparency for complex blending effects.
    • Layer Reordering: Change the render order of your tilemaps.

    ✅ Tileset Selector

    • Smart Selection: Drag and select a rectangular area of tiles.
    • Multiple Tilesets: Support for multiple tilesets per project (planned).
    • Auto-import: Automatically detects tile size from the imported image.

    ✅ Engine Integration

    • Workspace Selection: Link the editor to your PixelRoot32 projects directory.
    • Direct Export: Files are generated with the correct namespaces and structures for immediate use.
    • BPP Compatibility: Ensures exported data matches the engine's expected format for 1bpp, 2bpp, and 4bpp.

    Data Formats

    Project File (.pr32scene)

    The editor uses a custom JSON-based format to save your project state, including:

    • Tileset metadata (path, tile size, spacing).
    • Layer data (tile indices, width, height, position).
    • Project settings (BPP, namespace).

    Exported C++

    The editor generates .h and .cpp files containing:

    • Tilemap Data: Packed arrays of tile indices.
    • Tilemap Structures: pixelroot32::graphics::TileMap (or TileMap2bpp/TileMap4bpp) definitions.
    • Scene Headers: Convenient entry points for loading entire maps into your game.
    \ No newline at end of file diff --git a/site/tools/tilemap_editor/usage_guide/index.html b/site/tools/tilemap_editor/usage_guide/index.html index 5a7ed39..d363dd8 100644 --- a/site/tools/tilemap_editor/usage_guide/index.html +++ b/site/tools/tilemap_editor/usage_guide/index.html @@ -1 +1 @@ - Usage Guide - PixelRoot32 Documentation

    Usage Guide

    This guide covers the basic workflow for creating and exporting a tilemap using the PixelRoot32 Tilemap Editor.

    1. Creating a New Project

    1. Launch the editor.
    2. Go to File > New Project.
    3. Enter the project name and select the base Color Depth (BPP):
    4. 1bpp: Monochrome (2 colors).
    5. 2bpp: 4 colors.
    6. 4bpp: 16 colors.
    7. Set the Tile Size (e.g., 8x8, 16x16).

    2. Importing a Tileset

    1. In the Tileset panel, click on Load Tileset.
    2. Select a PNG image containing your tiles.
    3. The image will be sliced into tiles based on the tile size set in the project.

    3. Painting Tiles

    1. Select a tile (or a range of tiles) from the Tileset panel.
    2. Select the Brush tool (Shortcut: B).
    3. Click and drag on the canvas to paint.
    4. Use the Layers panel to switch between different layers.

    4. Selection and Transformations

    • Single Selection: Click on a tile in the tileset.
    • Area Selection: Click and drag in the tileset to select a rectangular block of tiles.
    • Pipette: Press P and click on the canvas to pick the tile under the cursor.

    5. Exporting to C++

    1. Ensure your Workspace Path is correctly set in Project Settings. This should point to your PixelRoot32 project's src or include directory.
    2. Click on File > Export.
    3. The editor will generate:
    4. <ProjectName>_data.h and .cpp: Containing the raw tile data and structures.
    5. <ProjectName>_scene.h: A high-level header to include in your game.

    6. Keyboard Shortcuts

    Shortcut Action
    B Brush Tool
    E Eraser Tool
    R Rectangle Fill Tool
    P Pipette Tool
    Space + Drag Pan Canvas
    Mouse Wheel Zoom In/Out
    Ctrl + N New Project
    Ctrl + S Save Project
    Ctrl + E Export Project
    Esc Close floating panels
    \ No newline at end of file + Usage Guide - PixelRoot32 Documentation

    Usage Guide

    This guide covers the basic workflow for creating and exporting a tilemap using the PixelRoot32 Tilemap Editor.

    1. Creating a New Project

    1. Launch the editor.
    2. Go to File > New Project.
    3. Enter the project name and select the base Color Depth (BPP):
    4. 1bpp: Monochrome (2 colors).
    5. 2bpp: 4 colors.
    6. 4bpp: 16 colors.
    7. Set the Tile Size (e.g., 8x8, 16x16).

    2. Importing a Tileset

    1. In the Tileset panel, click on Load Tileset.
    2. Select a PNG image containing your tiles.
    3. The image will be sliced into tiles based on the tile size set in the project.

    3. Painting Tiles

    1. Select a tile (or a range of tiles) from the Tileset panel.
    2. Select the Brush tool (Shortcut: B).
    3. Click and drag on the canvas to paint.
    4. Use the Layers panel to switch between different layers.

    4. Selection and Transformations

    • Single Selection: Click on a tile in the tileset.
    • Area Selection: Click and drag in the tileset to select a rectangular block of tiles.
    • Pipette: Press P and click on the canvas to pick the tile under the cursor.

    5. Exporting to C++

    1. Ensure your Workspace Path is correctly set in Project Settings. This should point to your PixelRoot32 project's src or include directory.
    2. Click on File > Export.
    3. The editor will generate:
    4. <ProjectName>_data.h and .cpp: Containing the raw tile data and structures.
    5. <ProjectName>_scene.h: A high-level header to include in your game.

    6. Keyboard Shortcuts

    Shortcut Action
    B Brush Tool
    E Eraser Tool
    R Rectangle Fill Tool
    P Pipette Tool
    Space + Drag Pan Canvas
    Mouse Wheel Zoom In/Out
    Ctrl + N New Project
    Ctrl + S Save Project
    Ctrl + E Export Project
    Esc Close floating panels
    \ No newline at end of file From 29f83ef1875faa7e5a7b6d40ff12be1fb68a83ac Mon Sep 17 00:00:00 2001 From: Gperez88 Date: Thu, 29 Jan 2026 03:18:47 -0400 Subject: [PATCH 4/6] docs: revise game examples guide for clarity and consistency Update the game examples guide to improve clarity by translating sections from Spanish to English, enhancing descriptions, and standardizing terminology. Adjust formatting for better readability and ensure all game examples are accurately represented. This includes updates to the Metroidvania and Space Invaders sections, as well as a general cleanup of the document. --- docs/reference/game_examples_guide.md | 82 ++++++++++++++------------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/docs/reference/game_examples_guide.md b/docs/reference/game_examples_guide.md index 0df17d6..f9eccd7 100644 --- a/docs/reference/game_examples_guide.md +++ b/docs/reference/game_examples_guide.md @@ -4,17 +4,17 @@ This guide analyzes the complete game examples included with PixelRoot32, explai ## Available Examples -PixelRoot32 (en el proyecto [PixelRoot32 Game Samples](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Sampless)) incluye estos juegos y demos: - -- **Metroidvania**: Plataformas 2D con tilemap 4bpp multicapa y colisión tile-based (requiere `PIXELROOT32_ENABLE_4BPP_SPRITES`; sin scroll/cámara) -- **Space Invaders**: Shooter completo con enemigos, proyectiles, búnkeres y audio -- **Pong**: Clásico con física y colisiones -- **BrickBreaker**: Estilo Breakout con partículas y audio avanzado -- **Snake**: Juego en rejilla con entity pooling -- **TicTacToe**: Turnos y IA simple -- **CameraDemo**: Plataformas con cámara y parallax -- **SpritesDemo**: Sprites 2bpp y 4bpp -- **TileMapDemo**: Tilemaps 4bpp (con viewport culling) +PixelRoot32 (in the [PixelRoot32 Game Samples](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Sampless) project) includes these games and demos: + +- **Metroidvania**: 2D platformer with multi-layer 4bpp tilemap and tile-based collision (requires `PIXELROOT32_ENABLE_4BPP_SPRITES`; no scroll/camera) +- **Space Invaders**: Full shooter with enemies, projectiles, bunkers and audio +- **Pong**: Classic with physics and collisions +- **BrickBreaker**: Breakout-style with particles and advanced audio +- **Snake**: Grid-based game with entity pooling +- **TicTacToe**: Turn-based with simple AI +- **CameraDemo**: Platformer with camera and parallax +- **SpritesDemo**: 2bpp and 4bpp sprites +- **TileMapDemo**: 4bpp tilemaps (with viewport culling) ## Space Invaders @@ -28,7 +28,7 @@ Space Invaders demonstrates a complete game with multiple systems: - **Actor Hierarchy**: `PlayerActor`, `AlienActor`, `ProjectileActor`, `BunkerActor` - **Collision System**: Uses collision layers for player, enemies, projectiles - **Audio Integration**: Sound effects for shooting, explosions, music -- **Background**: Starfield (patrón de estrellas en código) o tilemap +- **Background**: Starfield (code-generated star pattern) or tilemap ### Key Systems @@ -79,35 +79,37 @@ projectile->setCollisionMask(Layers::ALIEN | Layers::BUNKER); ## Metroidvania -**Ubicación**: `src/examples/Games/Metroidvania/` +**Location**: `src/examples/Games/Metroidvania/` -### Arquitectura +**Assets**: Sprites and tilesets for this example come from the [Tiny Metroidvania 8x8](https://kenmi-art.itch.io/metroidvania) pack by **Kenmi** ([kenmi-art.itch.io](https://kenmi-art.itch.io)). -Metroidvania es un ejemplo de plataformas 2D con tilemap multicapa y optimizaciones pensadas para ESP32. **No usa scroll ni cámara**; el nivel se dibuja con origen fijo (0,0). +### Architecture + +Metroidvania is a 2D platformer example with multi-layer tilemap and optimizations aimed at ESP32. **It does not use scroll or camera**; the level is drawn with a fixed origin (0,0). -- **Scene**: `MetroidvaniaScene` con un único `PlayerActor` y varias capas de tilemap (background, platforms, details, stairs). -- **PlayerActor**: Movimiento horizontal y vertical, escaleras, colisión **tile-based** (sin listas de rectángulos). -- **Tilemap**: 4bpp (`TileMap4bpp`), con viewport culling y caché de paleta en el motor. Nivel 40×30 tiles (320×240 px). -- **Sin cámara**: La vista no sigue al jugador; para scroll habría que usar `Camera2D` y aplicar offset en el renderer (como en CameraDemo). +- **Scene**: `MetroidvaniaScene` with a single `PlayerActor` and several tilemap layers (background, platforms, details, stairs). +- **PlayerActor**: Horizontal and vertical movement, stairs, **tile-based** collision (no rectangle lists). +- **Tilemap**: 4bpp (`TileMap4bpp`), with viewport culling and palette cache in the engine. Level 40×30 tiles (320×240 px). +- **No camera**: The view does not follow the player; for scroll you would use `Camera2D` and apply offset in the renderer (as in CameraDemo). -### Características del motor usadas +### Engine features used -- **Colisión tile-based**: Comprobación directa de tiles alrededor del jugador (`getTileAt`), en lugar de iterar sobre `platformRects`. -- **Sprites 4bpp**: Player con animaciones (idle, run, jump) desde headers generados (p. ej. Sprite Compiler). -- **Optimizaciones de renderizado**: Viewport culling en `drawTileMap`, `drawSprite` 4bpp optimizado, capas culleadas por viewport. -- **Opcional**: Scene arena, DMA, IRAM_ATTR en rutas críticas (según plan de optimización del ejemplo). +- **Tile-based collision**: Direct tile checks around the player (`getTileAt`), instead of iterating over `platformRects`. +- **4bpp sprites**: Player with animations (idle, run, jump) from generated headers (e.g. Sprite Compiler). +- **Rendering optimizations**: Viewport culling in `drawTileMap`, optimized 4bpp `drawSprite`, layers culled by viewport. +- **Optional**: Scene arena, DMA, IRAM_ATTR on critical paths (per example optimization plan). -### Patrones utilizados +### Patterns used -- **Tile-based collision**: Un solo acceso por tile en O(1) en lugar de O(N) rectángulos. -- **Detección de escaleras**: Un solo resultado reutilizado para colisión y cambio de estado. -- **Hitbox simplificada**: Menos puntos de comprobación vertical (cabeza y pies). +- **Tile-based collision**: Single O(1) access per tile instead of O(N) rectangles. +- **Stair detection**: Single result reused for collision and state change. +- **Simplified hitbox**: Fewer vertical check points (head and feet). -### Lecciones +### Lessons learned -- La colisión tile-based escala mejor que listas de rectángulos en niveles grandes. -- Las optimizaciones de viewport y 4bpp mejoran FPS en ESP32. -- Metroidvania sirve de referencia para juegos de plataformas con tilemap y cámara. +- Tile-based collision scales better than rectangle lists on large levels. +- Viewport and 4bpp optimizations improve FPS on ESP32. +- Metroidvania serves as a reference for platformers with tilemap and camera. ## Pong @@ -481,20 +483,20 @@ void draw(pixelroot32::graphics::Renderer& renderer) override { ### Beginner Examples -1. **Pong**: Física y colisiones básicas -2. **TicTacToe**: Lógica por turnos -3. **Snake**: Entity pooling y rejilla +1. **Pong**: Basic physics and collisions +2. **TicTacToe**: Turn-based logic +3. **Snake**: Entity pooling and grid ### Intermediate Examples -4. **CameraDemo**: Cámara y parallax -5. **SpritesDemo**: Formatos 2bpp y 4bpp -6. **BrickBreaker**: Física, partículas y audio +4. **CameraDemo**: Camera and parallax +5. **SpritesDemo**: 2bpp and 4bpp formats +6. **BrickBreaker**: Physics, particles and audio ### Advanced Examples -7. **Space Invaders**: Juego completo (sprites 1bpp, colisiones, audio) -8. **Metroidvania**: Plataformas con tilemap 4bpp multicapa, colisión tile-based y optimizaciones ESP32 (sin scroll/cámara) +7. **Space Invaders**: Full game (1bpp sprites, collisions, audio) +8. **Metroidvania**: Platformer with multi-layer 4bpp tilemap, tile-based collision and ESP32 optimizations (no scroll/camera) ## Code Study Recommendations From 04b0f07d369c99f1cd78d72e0bdc7baeae997575 Mon Sep 17 00:00:00 2001 From: Gperez88 Date: Thu, 29 Jan 2026 14:39:11 -0400 Subject: [PATCH 5/6] docs: enhance tilemap editor documentation with detailed export process and screen resolution information Update the tilemap editor documentation to clarify the export process, including namespace settings, color depth detection, and options for storing data in Flash for ESP32. Add a new section on screen resolution, specifying that all examples are configured for a 240x240 resolution. Improve overall clarity and structure of the usage guide. --- docs/reference/game_examples_guide.md | 14 +++++++++----- docs/tools/tilemap_editor/overview.md | 9 +++++---- docs/tools/tilemap_editor/usage_guide.md | 16 +++++++++++----- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/docs/reference/game_examples_guide.md b/docs/reference/game_examples_guide.md index f9eccd7..b167eb1 100644 --- a/docs/reference/game_examples_guide.md +++ b/docs/reference/game_examples_guide.md @@ -422,6 +422,10 @@ public: ## Common Patterns Across Examples +### Screen Resolution + +All examples are configured for a 240x240 screen resolution. + ### Scene Initialization All examples follow this pattern: @@ -489,14 +493,14 @@ void draw(pixelroot32::graphics::Renderer& renderer) override { ### Intermediate Examples -4. **CameraDemo**: Camera and parallax -5. **SpritesDemo**: 2bpp and 4bpp formats -6. **BrickBreaker**: Physics, particles and audio +1. **CameraDemo**: Camera and parallax +2. **SpritesDemo**: 2bpp and 4bpp formats +3. **BrickBreaker**: Physics, particles and audio ### Advanced Examples -7. **Space Invaders**: Full game (1bpp sprites, collisions, audio) -8. **Metroidvania**: Platformer with multi-layer 4bpp tilemap, tile-based collision and ESP32 optimizations (no scroll/camera) +1. **Space Invaders**: Full game (1bpp sprites, collisions, audio) +2. **Metroidvania**: Platformer with multi-layer 4bpp tilemap, tile-based collision and ESP32 optimizations (no scroll/camera) ## Code Study Recommendations diff --git a/docs/tools/tilemap_editor/overview.md b/docs/tools/tilemap_editor/overview.md index f05d704..a780988 100644 --- a/docs/tools/tilemap_editor/overview.md +++ b/docs/tools/tilemap_editor/overview.md @@ -51,8 +51,9 @@ The editor uses a custom JSON-based format to save your project state, including ### Exported C++ -The editor generates `.h` and `.cpp` files containing: +The editor generates `.h` and `.cpp` files containing: -- **Tilemap Data**: Packed arrays of tile indices. -- **Tilemap Structures**: `pixelroot32::graphics::TileMap` (or `TileMap2bpp`/`TileMap4bpp`) definitions. -- **Scene Headers**: Convenient entry points for loading entire maps into your game. +- **Tilemap Data**: One packed array of tile indices per layer (`*_INDICES[]`). Each layer is exposed as a `TileMap4bpp` (or `TileMap2bpp`/`TileMap`) with an `indices` pointer; use the same data for rendering and for tile-based collision in your game code. +- **Tilemap Structures**: `pixelroot32::graphics::TileMap` (or `TileMap2bpp`/`TileMap4bpp`) definitions, plus tileset pool and palette. +- **Export options**: **Store data in Flash (ESP32)** (default) emits static data with `PROGMEM` to reduce RAM use; **Legacy format** disables Flash attributes for backward compatibility or non-ESP32 builds. +- **Scene init**: Call `init()` once before drawing; the generated code registers the palette and configures each layer for the engine. diff --git a/docs/tools/tilemap_editor/usage_guide.md b/docs/tools/tilemap_editor/usage_guide.md index f33137c..5b270ec 100644 --- a/docs/tools/tilemap_editor/usage_guide.md +++ b/docs/tools/tilemap_editor/usage_guide.md @@ -33,11 +33,17 @@ This guide covers the basic workflow for creating and exporting a tilemap using ## 5. Exporting to C++ -1. Ensure your **Workspace Path** is correctly set in **Project Settings**. This should point to your PixelRoot32 project's `src` or `include` directory. -2. Click on **File > Export**. -3. The editor will generate: - - `_data.h` and `.cpp`: Containing the raw tile data and structures. - - `_scene.h`: A high-level header to include in your game. +1. Ensure you have at least one **tileset** imported. +2. Click the **Export** button in the top right (or **File > Export** / **Ctrl+E**). +3. In the export dialog: + - Set the **C++ Namespace** (e.g. `forest_level`); it defaults to the project name. + - Review the **Color Depth (BPP)**; it is auto-detected from your tileset colors (1bpp, 2bpp, or 4bpp). + - **Store data in Flash (ESP32)**: Checked by default; emits `PROGMEM` for palette, tileset, and layer data to reduce RAM on ESP32. + - **Legacy format (no Flash attribute)**: Use for older projects or non-ESP32 builds. +4. Click **Export Now** and choose the output directory. +5. The editor generates: + - `.h`: Declarations (e.g. `TILE_SIZE`, `MAP_WIDTH`, `MAP_HEIGHT`, layer `TileMap4bpp` externs, `init()`). + - `.cpp`: Definitions (palette, tileset pool, layer indices, `init()` implementation). Use each layer's `.indices` in your game for drawing and tile-based collision. ## 6. Keyboard Shortcuts From 26c9da942ed0e6df0115a32e208e591af08ce314 Mon Sep 17 00:00:00 2001 From: Gperez88 Date: Thu, 29 Jan 2026 15:16:54 -0400 Subject: [PATCH 6/6] docs: update audio documentation for performance and ESP32 considerations Revise the audio API reference documentation to enhance clarity on performance considerations and platform-specific details for ESP32. Include sections on channel limits, event management, and memory usage to guide users in optimizing audio playback. Ensure all relevant sections are updated for consistency and accuracy. --- .../audio/audio_config/index.html | 2 +- .../audio/audio_engine/index.html | 2 +- .../audio/audio_types/index.html | 2 +- .../audio/music_player/index.html | 2 +- site/api_reference/core/actor/index.html | 2 +- site/api_reference/core/engine/index.html | 54 ++++++++++--------- site/api_reference/core/entity/index.html | 2 +- .../core/input_config/index.html | 2 +- .../core/input_manager/index.html | 2 +- .../core/physics_actor/index.html | 2 +- site/api_reference/core/scene/index.html | 2 +- .../graphics/camera2d/index.html | 2 +- site/api_reference/graphics/color/index.html | 2 +- .../graphics/display_config/index.html | 2 +- site/api_reference/graphics/font/index.html | 2 +- .../graphics/renderer/index.html | 2 +- site/api_reference/graphics/sprite/index.html | 2 +- .../api_reference/graphics/tilemap/index.html | 2 +- .../physics/collision_system/index.html | 2 +- .../physics/collision_types/index.html | 2 +- site/api_reference/ui/ui_button/index.html | 2 +- site/api_reference/ui/ui_checkbox/index.html | 2 +- site/api_reference/ui/ui_element/index.html | 2 +- site/api_reference/ui/ui_label/index.html | 2 +- site/api_reference/ui/ui_layout/index.html | 2 +- .../ui/ui_layouts/anchor_layout/index.html | 2 +- .../ui/ui_layouts/grid_layout/index.html | 2 +- .../ui_layouts/horizontal_layout/index.html | 2 +- .../ui_layouts/padding_container/index.html | 2 +- .../ui/ui_layouts/panel/index.html | 2 +- .../ui/ui_layouts/vertical_layout/index.html | 2 +- .../fundamental_concepts/index.html | 2 +- site/getting_started/installation/index.html | 2 +- .../what_is_pixelroot32/index.html | 2 +- .../why_pixelroot32/index.html | 2 +- .../your_first_project/index.html | 2 +- site/index.html | 2 +- .../cameras_and_scrolling/index.html | 2 +- .../color_palettes/index.html | 2 +- .../particles_and_effects/index.html | 2 +- .../sprites_and_animation/index.html | 2 +- .../advanced_graphics/tilemaps/index.html | 2 +- site/manual/game_development/audio/index.html | 2 +- .../basic_rendering/index.html | 2 +- .../input_and_control/index.html | 2 +- .../physics_and_collisions/index.html | 2 +- .../scenes_and_entities/index.html | 2 +- .../user_interface/index.html | 2 +- .../optimization/extensibility/index.html | 2 +- .../optimization/memory_management/index.html | 2 +- .../performance_tuning/index.html | 4 +- .../platforms_and_drivers/index.html | 7 +-- site/reference/api_overview/index.html | 2 +- site/reference/code_examples/index.html | 2 +- site/reference/game_examples_guide/index.html | 16 +++--- site/resources/available_tools/index.html | 2 +- site/resources/faq/index.html | 2 +- .../limitations_and_considerations/index.html | 2 +- site/resources/troubleshooting/index.html | 2 +- site/search/search_index.json | 2 +- .../advanced_features/index.html | 2 +- .../sprite_compiler/installation/index.html | 2 +- .../tools/sprite_compiler/overview/index.html | 2 +- .../sprite_compiler/usage_guide/index.html | 2 +- .../tilemap_editor/installation/index.html | 2 +- site/tools/tilemap_editor/overview/index.html | 2 +- .../tilemap_editor/usage_guide/index.html | 2 +- 67 files changed, 105 insertions(+), 102 deletions(-) diff --git a/site/api_reference/audio/audio_config/index.html b/site/api_reference/audio/audio_config/index.html index 7b53c69..5e06d79 100644 --- a/site/api_reference/audio/audio_config/index.html +++ b/site/api_reference/audio/audio_config/index.html @@ -84,4 +84,4 @@ engine.init(); engine.run(); } -

    Platform-Specific Considerations

    ESP32 DAC Backend

    • Sample rate: 11025 Hz recommended (lower CPU usage)
    • Quality: Lower quality, but simple setup
    • Pin: Uses GPIO 25 or 26
    • Hardware: Requires simple amplifier circuit

    ESP32 I2S Backend

    • Sample rate: 22050 Hz recommended
    • Quality: Higher quality than DAC
    • Pins: Requires I2S pins (BCLK, LRCK, DOUT)
    • Hardware: Requires external I2S DAC

    Native SDL2 Backend

    • Sample rate: 44100 Hz typical
    • Quality: High quality
    • Setup: Requires SDL2 library installed
    • Platforms: Windows, Linux, macOS

    Performance Considerations

    • Sample rate: Lower rates use less CPU and memory
    • Backend choice: DAC is simpler but lower quality than I2S
    • Buffer size: Configured in backend, affects latency vs stability

    See Also

    \ No newline at end of file +

    Platform-Specific Considerations

    ESP32 DAC Backend

    • Sample rate: 11025 Hz recommended (lower CPU usage)
    • Quality: Lower quality, but simple setup
    • Pin: Uses GPIO 25 or 26
    • Hardware: Requires simple amplifier circuit

    ESP32 I2S Backend

    • Sample rate: 22050 Hz recommended
    • Quality: Higher quality than DAC
    • Pins: Requires I2S pins (BCLK, LRCK, DOUT)
    • Hardware: Requires external I2S DAC

    Native SDL2 Backend

    • Sample rate: 44100 Hz typical
    • Quality: High quality
    • Setup: Requires SDL2 library installed
    • Platforms: Windows, Linux, macOS

    Performance Considerations

    • Sample rate: Lower rates use less CPU and memory
    • Backend choice: DAC is simpler but lower quality than I2S
    • Buffer size: Configured in backend, affects latency vs stability

    See Also

    \ No newline at end of file diff --git a/site/api_reference/audio/audio_engine/index.html b/site/api_reference/audio/audio_engine/index.html index f7bab94..08ef618 100644 --- a/site/api_reference/audio/audio_engine/index.html +++ b/site/api_reference/audio/audio_engine/index.html @@ -88,4 +88,4 @@ } } }; -

    Performance Considerations

    • Channel limit: Only 4 channels total; plan sound effects accordingly
    • Event dropping: If all channels are busy, new events are silently dropped
    • Update frequency: update() must be called every frame for proper timing
    • Sample generation: generateSamples() is called by backend at audio rate (not game rate)

    ESP32 Considerations

    • Sample rate: Lower sample rates (11025 Hz) use less CPU and memory
    • Backend choice: DAC backend is simpler but lower quality than I2S
    • Buffer size: Larger buffers reduce underruns but increase latency
    • Channel management: Limit simultaneous sounds to avoid channel conflicts

    See Also

    \ No newline at end of file +

    Performance Considerations

    • Channel limit: Only 4 channels total; plan sound effects accordingly
    • Event dropping: If all channels are busy, new events are silently dropped
    • Update frequency: update() must be called every frame for proper timing
    • Sample generation: generateSamples() is called by backend at audio rate (not game rate)

    ESP32 Considerations

    • Sample rate: Lower sample rates (11025 Hz) use less CPU and memory
    • Backend choice: DAC backend is simpler but lower quality than I2S
    • Buffer size: Larger buffers reduce underruns but increase latency
    • Channel management: Limit simultaneous sounds to avoid channel conflicts

    See Also

    \ No newline at end of file diff --git a/site/api_reference/audio/audio_types/index.html b/site/api_reference/audio/audio_types/index.html index 50f247c..6b8a9e6 100644 --- a/site/api_reference/audio/audio_types/index.html +++ b/site/api_reference/audio/audio_types/index.html @@ -97,4 +97,4 @@ delay(50); // Small delay between events } } -

    Performance Considerations

    • Event creation: Creating events is fast (just struct initialization)
    • Channel allocation: Events are queued and played when channels are available
    • Frequency range: Keep frequencies in reasonable range (100-5000 Hz) for best results
    • Duration: Shorter durations free channels faster

    ESP32 Considerations

    • Memory: Events are small structs, safe to create frequently
    • CPU: Audio generation is efficient but limit simultaneous sounds
    • Quality: Lower sample rates reduce CPU usage

    See Also

    \ No newline at end of file +

    Performance Considerations

    • Event creation: Creating events is fast (just struct initialization)
    • Channel allocation: Events are queued and played when channels are available
    • Frequency range: Keep frequencies in reasonable range (100-5000 Hz) for best results
    • Duration: Shorter durations free channels faster

    ESP32 Considerations

    • Memory: Events are small structs, safe to create frequently
    • CPU: Audio generation is efficient but limit simultaneous sounds
    • Quality: Lower sample rates reduce CPU usage

    See Also

    \ No newline at end of file diff --git a/site/api_reference/audio/music_player/index.html b/site/api_reference/audio/music_player/index.html index f402685..189d396 100644 --- a/site/api_reference/audio/music_player/index.html +++ b/site/api_reference/audio/music_player/index.html @@ -106,4 +106,4 @@ music.stop(); } }; -

    Performance Considerations

    • One channel: Music uses one channel, leaving others for sound effects
    • Update frequency: update() must be called every frame
    • Track size: Larger tracks use more memory (store in flash)
    • Tempo factor: Changing tempo is fast (just a multiplier)

    ESP32 Considerations

    • Memory: Store tracks in flash (const/constexpr) to save RAM
    • CPU: Music playback is lightweight (simple sequencing)
    • Channel conflict: Music and sound effects share channels; plan accordingly

    See Also

    \ No newline at end of file +

    Performance Considerations

    • One channel: Music uses one channel, leaving others for sound effects
    • Update frequency: update() must be called every frame
    • Track size: Larger tracks use more memory (store in flash)
    • Tempo factor: Changing tempo is fast (just a multiplier)

    ESP32 Considerations

    • Memory: Store tracks in flash (const/constexpr) to save RAM
    • CPU: Music playback is lightweight (simple sequencing)
    • Channel conflict: Music and sound effects share channels; plan accordingly

    See Also

    \ No newline at end of file diff --git a/site/api_reference/core/actor/index.html b/site/api_reference/core/actor/index.html index dace5aa..62e0cdb 100644 --- a/site/api_reference/core/actor/index.html +++ b/site/api_reference/core/actor/index.html @@ -126,4 +126,4 @@ } } }; -

    Performance Considerations

    • Collision layers: Use layers efficiently to reduce collision checks
    • Hitbox size: Keep hitboxes simple (AABB) for best performance
    • Collision callbacks: Keep onCollision() fast; avoid expensive operations
    • Layer organization: Group actors by layer to minimize checks

    ESP32 Considerations

    • Collision checks: Collision system automatically optimizes using layers
    • Memory: Each actor consumes memory; stay within MAX_ENTITIES limit
    • Object pooling: Reuse actors instead of creating/destroying frequently

    See Also

    \ No newline at end of file +

    Performance Considerations

    • Collision layers: Use layers efficiently to reduce collision checks
    • Hitbox size: Keep hitboxes simple (AABB) for best performance
    • Collision callbacks: Keep onCollision() fast; avoid expensive operations
    • Layer organization: Group actors by layer to minimize checks

    ESP32 Considerations

    • Collision checks: Collision system automatically optimizes using layers
    • Memory: Each actor consumes memory; stay within MAX_ENTITIES limit
    • Object pooling: Reuse actors instead of creating/destroying frequently

    See Also

    \ No newline at end of file diff --git a/site/api_reference/core/engine/index.html b/site/api_reference/core/engine/index.html index 317fba2..1bad076 100644 --- a/site/api_reference/core/engine/index.html +++ b/site/api_reference/core/engine/index.html @@ -1,4 +1,4 @@ - Engine - PixelRoot32 Documentation

    Engine

    The main engine class that manages the game loop and core subsystems.

    Description

    Engine acts as the central hub of the PixelRoot32 game engine. It initializes and manages the Renderer, InputManager, AudioEngine, and SceneManager. It runs the main game loop, handling timing (delta time), updating the current scene, and rendering frames.

    The engine provides a unified interface for both ESP32 and Native (SDL2) platforms, abstracting platform-specific details while maintaining consistent behavior.

    Namespace

    namespace pixelroot32::core {
    + Engine - PixelRoot32 Documentation      

    Engine

    The main engine class that manages the game loop and core subsystems.

    Description

    Engine acts as the central hub of the PixelRoot32 game engine. It initializes and manages the Renderer, InputManager, AudioEngine, and SceneManager. It runs the main game loop, handling timing (delta time), updating the current scene, and rendering frames.

    The engine provides a unified interface for both ESP32 and Native (SDL2) platforms, abstracting platform-specific details while maintaining consistent behavior.

    Namespace

    namespace pixelroot32::core {
         class Engine {
             // ...
         };
    @@ -85,28 +85,30 @@
         auto& music = engine.getMusicPlayer();
         music.playTrack(myMusicTrack);
     }
    -

    Usage Example

    #include "core/Engine.h"
    -#include "graphics/DisplayConfig.h"
    -#include "MyScene.h"
    -
    -void setup() {
    -    // Configure display
    -    pixelroot32::graphics::DisplayConfig displayConfig;
    -    displayConfig.width = 128;
    -    displayConfig.height = 128;
    -    displayConfig.rotation = 0;
    -
    -    // Create engine
    -    pixelroot32::core::Engine engine(displayConfig);
    -
    -    // Initialize
    -    engine.init();
    -
    -    // Create and set scene
    -    MyScene myScene;
    -    engine.setScene(&myScene);
    -
    -    // Run game loop
    -    engine.run();
    -}
    -

    Performance Considerations

    • Initialization: init() should be called once at startup, not in the game loop
    • Scene switching: Switching scenes is fast but avoid doing it every frame
    • Subsystem access: Getters are inline and very fast; safe to call every frame
    • Delta time: Use getDeltaTime() for frame-rate independent movement

    ESP32 Considerations

    • Ensure init() completes before run() to avoid initialization issues
    • Monitor memory usage when switching scenes frequently
    • Use getDeltaTime() for consistent gameplay across different frame rates

    See Also

    \ No newline at end of file +

    Optional: FPS overlay

    When the engine is built with the preprocessor define PIXELROOT32_ENABLE_FPS_DISPLAY, an on-screen FPS counter is drawn each frame.

    Behavior:

    • A green text string "FPS xxx" is drawn in the top-right area (position from Renderer::getWidth() and a fixed Y offset).
    • The value is derived from frame delta time (FPS = 1000 / deltaTime ms), clamped to 0–999.

    Performance:

    • The numeric value is recalculated and formatted only every 8 frames; the cached string is drawn every frame to keep the overlay visible without extra per-frame cost (division and snprintf are done at most once every 8 frames).

    How to enable:

    In platformio.ini, add to your environment's build_flags:

    build_flags =
    +    -D PIXELROOT32_ENABLE_FPS_DISPLAY
    +

    No code changes are required; the overlay is drawn automatically after the scene in Engine::draw(). The implementation uses the private method drawFpsOverlay(Renderer& r), which is only compiled when the define is set.

    See also: Performance Tuning - Profiling and Platforms and Drivers - Build flags.

    Usage Example

    #include "core/Engine.h"
    +#include "graphics/DisplayConfig.h"
    +#include "MyScene.h"
    +
    +void setup() {
    +    // Configure display
    +    pixelroot32::graphics::DisplayConfig displayConfig;
    +    displayConfig.width = 128;
    +    displayConfig.height = 128;
    +    displayConfig.rotation = 0;
    +
    +    // Create engine
    +    pixelroot32::core::Engine engine(displayConfig);
    +
    +    // Initialize
    +    engine.init();
    +
    +    // Create and set scene
    +    MyScene myScene;
    +    engine.setScene(&myScene);
    +
    +    // Run game loop
    +    engine.run();
    +}
    +

    Performance Considerations

    • Initialization: init() should be called once at startup, not in the game loop
    • Scene switching: Switching scenes is fast but avoid doing it every frame
    • Subsystem access: Getters are inline and very fast; safe to call every frame
    • Delta time: Use getDeltaTime() for frame-rate independent movement

    ESP32 Considerations

    • Ensure init() completes before run() to avoid initialization issues
    • Monitor memory usage when switching scenes frequently
    • Use getDeltaTime() for consistent gameplay across different frame rates

    See Also

    \ No newline at end of file diff --git a/site/api_reference/core/entity/index.html b/site/api_reference/core/entity/index.html index 3bae716..f63014a 100644 --- a/site/api_reference/core/entity/index.html +++ b/site/api_reference/core/entity/index.html @@ -84,4 +84,4 @@ private: float rotation = 0.0f; }; -

    Performance Considerations

    • Visibility: Use isVisible = false instead of removing entities when hiding
    • Enable state: Use isEnabled = false to pause entity logic
    • Render layers: Organize entities by layer to minimize layer switches
    • Direct access: Direct property access is fast (no function call overhead)

    ESP32 Considerations

    • Memory: Each entity consumes memory; stay within MAX_ENTITIES limit
    • Object pooling: Reuse entities instead of creating/destroying frequently
    • Update frequency: Disable entities that don't need to update every frame

    See Also

    \ No newline at end of file +

    Performance Considerations

    • Visibility: Use isVisible = false instead of removing entities when hiding
    • Enable state: Use isEnabled = false to pause entity logic
    • Render layers: Organize entities by layer to minimize layer switches
    • Direct access: Direct property access is fast (no function call overhead)

    ESP32 Considerations

    • Memory: Each entity consumes memory; stay within MAX_ENTITIES limit
    • Object pooling: Reuse entities instead of creating/destroying frequently
    • Update frequency: Disable entities that don't need to update every frame

    See Also

    \ No newline at end of file diff --git a/site/api_reference/core/input_config/index.html b/site/api_reference/core/input_config/index.html index 2b55018..2ae3e1e 100644 --- a/site/api_reference/core/input_config/index.html +++ b/site/api_reference/core/input_config/index.html @@ -117,4 +117,4 @@ SDL_SCANCODE_SPACE, // Jump SDL_SCANCODE_RETURN // Action ); -

    Performance Considerations

    • Memory: Arrays are allocated dynamically (small overhead)
    • Configuration: Done once at startup, no runtime cost
    • Access: Button indices are fast (array access)

    ESP32 Considerations

    • Pin configuration: Ensure pins are not used by other peripherals
    • Debouncing: Hardware debouncing recommended for reliable input
    • Power: Buttons should use pull-up resistors to avoid floating pins

    See Also

    \ No newline at end of file +

    Performance Considerations

    • Memory: Arrays are allocated dynamically (small overhead)
    • Configuration: Done once at startup, no runtime cost
    • Access: Button indices are fast (array access)

    ESP32 Considerations

    • Pin configuration: Ensure pins are not used by other peripherals
    • Debouncing: Hardware debouncing recommended for reliable input
    • Power: Buttons should use pull-up resistors to avoid floating pins

    See Also

    \ No newline at end of file diff --git a/site/api_reference/core/input_manager/index.html b/site/api_reference/core/input_manager/index.html index 06d233c..f85711e 100644 --- a/site/api_reference/core/input_manager/index.html +++ b/site/api_reference/core/input_manager/index.html @@ -126,4 +126,4 @@ } } }; -

    Input State Comparison

    Method Returns true when Use Case
    isButtonPressed() Button just pressed this frame One-time actions (jump, shoot)
    isButtonReleased() Button just released this frame Release events (stop charging)
    isButtonClicked() Button pressed then released UI buttons, menu selection
    isButtonDown() Button currently held Continuous actions (movement)

    Performance Considerations

    • Update frequency: update() must be called every frame
    • Debouncing: Handled automatically, no performance impact
    • State queries: All query methods are fast (inline accessors)
    • Memory: Button state arrays are small and efficient

    ESP32 Considerations

    • GPIO pins: Configure pins in InputConfig
    • Pull-up/pull-down: Ensure proper resistor configuration
    • Debouncing: Hardware debouncing recommended for noisy buttons
    • Pin limits: Some ESP32 pins have restrictions (check datasheet)

    Native Considerations

    • Keyboard mapping: Uses SDL scancodes
    • Key detection: Automatically handles keyboard state
    • Multiple keys: Can detect multiple keys simultaneously

    See Also

    \ No newline at end of file +

    Input State Comparison

    Method Returns true when Use Case
    isButtonPressed() Button just pressed this frame One-time actions (jump, shoot)
    isButtonReleased() Button just released this frame Release events (stop charging)
    isButtonClicked() Button pressed then released UI buttons, menu selection
    isButtonDown() Button currently held Continuous actions (movement)

    Performance Considerations

    • Update frequency: update() must be called every frame
    • Debouncing: Handled automatically, no performance impact
    • State queries: All query methods are fast (inline accessors)
    • Memory: Button state arrays are small and efficient

    ESP32 Considerations

    • GPIO pins: Configure pins in InputConfig
    • Pull-up/pull-down: Ensure proper resistor configuration
    • Debouncing: Hardware debouncing recommended for noisy buttons
    • Pin limits: Some ESP32 pins have restrictions (check datasheet)

    Native Considerations

    • Keyboard mapping: Uses SDL scancodes
    • Key detection: Automatically handles keyboard state
    • Multiple keys: Can detect multiple keys simultaneously

    See Also

    \ No newline at end of file diff --git a/site/api_reference/core/physics_actor/index.html b/site/api_reference/core/physics_actor/index.html index 5c89ff9..46b7d6b 100644 --- a/site/api_reference/core/physics_actor/index.html +++ b/site/api_reference/core/physics_actor/index.html @@ -154,4 +154,4 @@ playBounceSound(); } }; -

    Performance Considerations

    • Physics integration: Very efficient (simple velocity integration)
    • World bounds: Boundary checks are fast (AABB)
    • Friction: Applied every frame; keep friction values reasonable
    • Collision callbacks: Keep onCollision() and onWorldCollision() fast

    ESP32 Considerations

    • Floating point: Uses float math; acceptable for ESP32 but integer math would be faster
    • Frame rate: Physics is frame-rate independent (uses deltaTime)
    • Memory: Each PhysicsActor consumes more memory than Actor (velocity, limits, etc.)

    See Also

    \ No newline at end of file +

    Performance Considerations

    • Physics integration: Very efficient (simple velocity integration)
    • World bounds: Boundary checks are fast (AABB)
    • Friction: Applied every frame; keep friction values reasonable
    • Collision callbacks: Keep onCollision() and onWorldCollision() fast

    ESP32 Considerations

    • Floating point: Uses float math; acceptable for ESP32 but integer math would be faster
    • Frame rate: Physics is frame-rate independent (uses deltaTime)
    • Memory: Each PhysicsActor consumes more memory than Actor (velocity, limits, etc.)

    See Also

    \ No newline at end of file diff --git a/site/api_reference/core/scene/index.html b/site/api_reference/core/scene/index.html index e235e23..0b7713d 100644 --- a/site/api_reference/core/scene/index.html +++ b/site/api_reference/core/scene/index.html @@ -128,4 +128,4 @@ renderer.drawText(scoreText, 10, 10, Color::White, 1); } }; -

    Performance Considerations

    • Entity limit: MAX_ENTITIES (default 32) can be overridden via compiler flags; plan accordingly
    • Add/Remove: Frequent add/remove operations can be expensive; use object pooling
    • Update order: Entities are updated in add order; consider order for dependencies
    • Collision checks: CollisionSystem automatically handles actor collisions efficiently

    ESP32 Considerations

    • Memory: Each entity consumes memory; stay well below the limit
    • Object pooling: Essential for ESP32 to avoid memory fragmentation
    • Scene switching: Clearing and recreating scenes can fragment memory; reuse scenes when possible

    See Also

    \ No newline at end of file +

    Performance Considerations

    • Entity limit: MAX_ENTITIES (default 32) can be overridden via compiler flags; plan accordingly
    • Add/Remove: Frequent add/remove operations can be expensive; use object pooling
    • Update order: Entities are updated in add order; consider order for dependencies
    • Collision checks: CollisionSystem automatically handles actor collisions efficiently

    ESP32 Considerations

    • Memory: Each entity consumes memory; stay well below the limit
    • Object pooling: Essential for ESP32 to avoid memory fragmentation
    • Scene switching: Clearing and recreating scenes can fragment memory; reuse scenes when possible

    See Also

    \ No newline at end of file diff --git a/site/api_reference/graphics/camera2d/index.html b/site/api_reference/graphics/camera2d/index.html index 3f4b1e3..abbddc2 100644 --- a/site/api_reference/graphics/camera2d/index.html +++ b/site/api_reference/graphics/camera2d/index.html @@ -120,4 +120,4 @@ renderer.setDisplayOffset(0, 0); renderer.drawText("Score: 100", 10, 10, Color::White, 1); } -

    Performance Considerations

    • Apply frequency: apply() is fast; safe to call every frame
    • Boundary checks: Boundary clamping is efficient
    • Following: Dead-zone calculations are lightweight

    ESP32 Considerations

    • Float math: Uses floating point; acceptable but integer math would be faster
    • Memory: Camera is small (few floats); minimal memory usage

    See Also

    \ No newline at end of file +

    Performance Considerations

    • Apply frequency: apply() is fast; safe to call every frame
    • Boundary checks: Boundary clamping is efficient
    • Following: Dead-zone calculations are lightweight

    ESP32 Considerations

    • Float math: Uses floating point; acceptable but integer math would be faster
    • Memory: Camera is small (few floats); minimal memory usage

    See Also

    \ No newline at end of file diff --git a/site/api_reference/graphics/color/index.html b/site/api_reference/graphics/color/index.html index 99fd85c..75430fb 100644 --- a/site/api_reference/graphics/color/index.html +++ b/site/api_reference/graphics/color/index.html @@ -70,4 +70,4 @@ pixelroot32::graphics::enableDualPaletteMode(true); pixelroot32::graphics::setBackgroundCustomPalette(CUSTOM_PALETTE); pixelroot32::graphics::setSpriteCustomPalette(CUSTOM_PALETTE); -

    Performance Considerations

    • Color resolution: Fast lookup operation
    • Palette switching: Changing palettes is fast (just pointer assignment)
    • Memory: Palettes are stored in flash (const arrays) for best performance
    • Dual mode: Slightly more overhead than legacy mode, but minimal

    ESP32 Considerations

    • Flash storage: Store custom palettes in flash (const/constexpr)
    • Memory: Palettes are small (16 uint16_t = 32 bytes)
    • Palette switching: Avoid switching palettes every frame

    See Also

    \ No newline at end of file +

    Performance Considerations

    • Color resolution: Fast lookup operation
    • Palette switching: Changing palettes is fast (just pointer assignment)
    • Memory: Palettes are stored in flash (const arrays) for best performance
    • Dual mode: Slightly more overhead than legacy mode, but minimal

    ESP32 Considerations

    • Flash storage: Store custom palettes in flash (const/constexpr)
    • Memory: Palettes are small (16 uint16_t = 32 bytes)
    • Palette switching: Avoid switching palettes every frame

    See Also

    \ No newline at end of file diff --git a/site/api_reference/graphics/display_config/index.html b/site/api_reference/graphics/display_config/index.html index 2bf3117..9aa24d3 100644 --- a/site/api_reference/graphics/display_config/index.html +++ b/site/api_reference/graphics/display_config/index.html @@ -87,4 +87,4 @@ -DTFT_WIDTH=240 -DTFT_HEIGHT=240 # ... pin configuration -

    Pin Configuration

    GPIO pins must be configured separately (not in DisplayConfig):

    • MOSI: Data pin
    • SCLK: Clock pin
    • DC: Data/Command pin
    • RST: Reset pin
    • CS: Chip select pin (optional)

    See Also

    \ No newline at end of file +

    Pin Configuration

    GPIO pins must be configured separately (not in DisplayConfig):

    • MOSI: Data pin
    • SCLK: Clock pin
    • DC: Data/Command pin
    • RST: Reset pin
    • CS: Chip select pin (optional)

    See Also

    \ No newline at end of file diff --git a/site/api_reference/graphics/font/index.html b/site/api_reference/graphics/font/index.html index 06f4278..f62dc14 100644 --- a/site/api_reference/graphics/font/index.html +++ b/site/api_reference/graphics/font/index.html @@ -121,4 +121,4 @@ int getTextHeight(const Font* font) { return font ? font->lineHeight : 8; } -

    Performance Considerations

    • Font storage: Store fonts in flash (const/constexpr) for best performance
    • Glyph lookup: Fast array access (character code - firstChar)
    • Fixed width: Fixed-width fonts are faster than variable-width
    • Font switching: Changing fonts is fast (just pointer assignment)

    ESP32 Considerations

    • Memory: Store font data in flash, not RAM
    • Font size: Larger fonts use more flash memory
    • Character range: Limit character range to save memory if not needed

    See Also

    \ No newline at end of file +

    Performance Considerations

    • Font storage: Store fonts in flash (const/constexpr) for best performance
    • Glyph lookup: Fast array access (character code - firstChar)
    • Fixed width: Fixed-width fonts are faster than variable-width
    • Font switching: Changing fonts is fast (just pointer assignment)

    ESP32 Considerations

    • Memory: Store font data in flash, not RAM
    • Font size: Larger fonts use more flash memory
    • Character range: Limit character range to save memory if not needed

    See Also

    \ No newline at end of file diff --git a/site/api_reference/graphics/renderer/index.html b/site/api_reference/graphics/renderer/index.html index 63be19d..7543b8d 100644 --- a/site/api_reference/graphics/renderer/index.html +++ b/site/api_reference/graphics/renderer/index.html @@ -100,4 +100,4 @@ renderer.endFrame(); } -

    Performance Considerations

    • Integer-only math: All operations use integer arithmetic for ESP32 efficiency
    • Sprite storage: Store sprite data in flash (const/constexpr) for best performance
    • Batch operations: Group similar draw calls together
    • Tilemaps: Dibuja un mapa de tiles completo. Implementa viewport culling automático y caché de paleta para máximo rendimiento.
    • Sprites 2bpp/4bpp: Optimizado para ESP32 (IRAM + acceso de 16 bits).

    ESP32 Considerations

    • Memory: Sprite data should be in flash, not RAM
    • Frame rate: Limit draw calls per frame for consistent FPS
    • Display offset: Use for scrolling instead of redrawing everything

    See Also

    \ No newline at end of file +

    Performance Considerations

    • Integer-only math: All operations use integer arithmetic for ESP32 efficiency
    • Sprite storage: Store sprite data in flash (const/constexpr) for best performance
    • Batch operations: Group similar draw calls together
    • Tilemaps: Dibuja un mapa de tiles completo. Implementa viewport culling automático y caché de paleta para máximo rendimiento.
    • Sprites 2bpp/4bpp: Optimizado para ESP32 (IRAM + acceso de 16 bits).

    ESP32 Considerations

    • Memory: Sprite data should be in flash, not RAM
    • Frame rate: Limit draw calls per frame for consistent FPS
    • Display offset: Use for scrolling instead of redrawing everything

    See Also

    \ No newline at end of file diff --git a/site/api_reference/graphics/sprite/index.html b/site/api_reference/graphics/sprite/index.html index ccd770b..89b4e0c 100644 --- a/site/api_reference/graphics/sprite/index.html +++ b/site/api_reference/graphics/sprite/index.html @@ -153,4 +153,4 @@ // Draw flipped renderer.drawSprite(sprite, 100, 100, Color::White, true); -

    Performance Considerations

    • 1bpp sprites: Most efficient (integer-only operations)
    • MultiSprite: Each layer is a separate draw call (still efficient)
    • 2bpp/4bpp: Experimental, uses more memory and CPU
    • Storage: Store sprite data in flash (const/constexpr) for best performance
    • Size limit: Sprites are limited to 16 pixels wide for 1bpp format

    ESP32 Considerations

    • Memory: Store sprite data in flash, not RAM
    • Sprite size: Smaller sprites = faster drawing
    • Format choice: Use 1bpp when possible for best performance
    • MultiSprite: More layers = more draw calls (but acceptable)

    See Also

    \ No newline at end of file +

    Performance Considerations

    • 1bpp sprites: Most efficient (integer-only operations)
    • MultiSprite: Each layer is a separate draw call (still efficient)
    • 2bpp/4bpp: Experimental, uses more memory and CPU
    • Storage: Store sprite data in flash (const/constexpr) for best performance
    • Size limit: Sprites are limited to 16 pixels wide for 1bpp format

    ESP32 Considerations

    • Memory: Store sprite data in flash, not RAM
    • Sprite size: Smaller sprites = faster drawing
    • Format choice: Use 1bpp when possible for best performance
    • MultiSprite: More layers = more draw calls (but acceptable)

    See Also

    \ No newline at end of file diff --git a/site/api_reference/graphics/tilemap/index.html b/site/api_reference/graphics/tilemap/index.html index 74c483e..2ca131e 100644 --- a/site/api_reference/graphics/tilemap/index.html +++ b/site/api_reference/graphics/tilemap/index.html @@ -182,4 +182,4 @@ return (tile == 1); // Wall tile } }; -

    Performance Considerations

    • Viewport culling: Only visible tiles are drawn (automatic)
    • Tile reuse: Reuse tile sprites across the map
    • Index storage: Compact uint8_t indices (1 byte per tile)
    • Memory: Store indices and tiles in flash (const) for best performance
    • Tile size: Smaller tiles = more tiles to draw, but more detail

    ESP32 Considerations

    • Memory: Store tilemap data in flash, not RAM
    • Map size: Large maps use more flash memory
    • Tile count: Limit unique tiles to save memory
    • Culling: Viewport culling is essential for large levels

    See Also

    \ No newline at end of file +

    Performance Considerations

    • Viewport culling: Only visible tiles are drawn (automatic)
    • Tile reuse: Reuse tile sprites across the map
    • Index storage: Compact uint8_t indices (1 byte per tile)
    • Memory: Store indices and tiles in flash (const) for best performance
    • Tile size: Smaller tiles = more tiles to draw, but more detail

    ESP32 Considerations

    • Memory: Store tilemap data in flash, not RAM
    • Map size: Large maps use more flash memory
    • Tile count: Limit unique tiles to save memory
    • Culling: Viewport culling is essential for large levels

    See Also

    \ No newline at end of file diff --git a/site/api_reference/physics/collision_system/index.html b/site/api_reference/physics/collision_system/index.html index b9ed24c..5bba407 100644 --- a/site/api_reference/physics/collision_system/index.html +++ b/site/api_reference/physics/collision_system/index.html @@ -59,4 +59,4 @@ Scene::update(deltaTime); } }; -

    Performance Considerations

    • Layer filtering: Very efficient; avoids most collision checks
    • AABB checks: Fast (simple rectangle intersection)
    • Pair checking: O(n²) complexity, but n is limited (MAX_ENTITIES = 32)
    • Update frequency: Called every frame; keep hitboxes simple

    ESP32 Considerations

    • Entity limit: MAX_ENTITIES = 32 limits collision pairs
    • Layer efficiency: Use layers effectively to minimize checks
    • Hitbox simplicity: Keep hitboxes as simple AABB for best performance

    See Also

    \ No newline at end of file +

    Performance Considerations

    • Layer filtering: Very efficient; avoids most collision checks
    • AABB checks: Fast (simple rectangle intersection)
    • Pair checking: O(n²) complexity, but n is limited (MAX_ENTITIES = 32)
    • Update frequency: Called every frame; keep hitboxes simple

    ESP32 Considerations

    • Entity limit: MAX_ENTITIES = 32 limits collision pairs
    • Layer efficiency: Use layers effectively to minimize checks
    • Hitbox simplicity: Keep hitboxes as simple AABB for best performance

    See Also

    \ No newline at end of file diff --git a/site/api_reference/physics/collision_types/index.html b/site/api_reference/physics/collision_types/index.html index c821306..e5ef454 100644 --- a/site/api_reference/physics/collision_types/index.html +++ b/site/api_reference/physics/collision_types/index.html @@ -99,4 +99,4 @@ return false; } }; -

    Performance Considerations

    • AABB checks: Very fast (simple rectangle intersection)
    • Circle checks: Slightly slower (distance calculation)
    • Sweep tests: More expensive (use only for fast-moving objects)
    • Layer filtering: Essential for performance with many actors

    ESP32 Considerations

    • Float math: Uses floating point; acceptable but integer math would be faster
    • Sweep tests: Use sparingly (more CPU intensive)
    • Layer efficiency: Use layers effectively to minimize checks

    See Also

    \ No newline at end of file +

    Performance Considerations

    • AABB checks: Very fast (simple rectangle intersection)
    • Circle checks: Slightly slower (distance calculation)
    • Sweep tests: More expensive (use only for fast-moving objects)
    • Layer filtering: Essential for performance with many actors

    ESP32 Considerations

    • Float math: Uses floating point; acceptable but integer math would be faster
    • Sweep tests: Use sparingly (more CPU intensive)
    • Layer efficiency: Use layers effectively to minimize checks

    See Also

    \ No newline at end of file diff --git a/site/api_reference/ui/ui_button/index.html b/site/api_reference/ui/ui_button/index.html index bb32124..89bb34e 100644 --- a/site/api_reference/ui/ui_button/index.html +++ b/site/api_reference/ui/ui_button/index.html @@ -131,4 +131,4 @@ // D-pad navigation is automatic // UP/DOWN moves selection // Action button (A) triggers selected button -

    Performance Considerations

    • Input handling: handleInput() is fast; safe to call every frame
    • Rendering: Simple rectangle and text; very efficient
    • Memory: Each button consumes memory (stay within MAX_ENTITIES)

    ESP32 Considerations

    • String storage: Button labels use std::string; consider memory usage
    • Callback functions: Use function pointers or lambdas (both efficient)

    See Also

    \ No newline at end of file +

    Performance Considerations

    • Input handling: handleInput() is fast; safe to call every frame
    • Rendering: Simple rectangle and text; very efficient
    • Memory: Each button consumes memory (stay within MAX_ENTITIES)

    ESP32 Considerations

    • String storage: Button labels use std::string; consider memory usage
    • Callback functions: Use function pointers or lambdas (both efficient)

    See Also

    \ No newline at end of file diff --git a/site/api_reference/ui/ui_checkbox/index.html b/site/api_reference/ui/ui_checkbox/index.html index 3865323..73ad48f 100644 --- a/site/api_reference/ui/ui_checkbox/index.html +++ b/site/api_reference/ui/ui_checkbox/index.html @@ -26,4 +26,4 @@

    Public Methods

    void setStyle(Color textCol, Color bgCol, bool drawBg = false)

    Configures the checkbox's visual style.

    Parameters: - textCol (Color): Color of the text - bgCol (Color): Color of the background - drawBg (bool, optional): Whether to draw the background rectangle. Default: false

    Returns: - void

    void setChecked(bool checked)

    Sets the checked state.

    Parameters: - checked (bool): True if checked

    Returns: - void

    bool isChecked() const

    Checks if the checkbox is currently checked.

    Returns: - bool: true if checked

    void toggle()

    Toggles the checkbox state and triggers the callback.

    Returns: - void

    void setSelected(bool selected)

    Sets the selection state (e.g., focused via D-pad).

    Parameters: - selected (bool): True if selected

    Returns: - void

    bool getSelected() const

    Checks if the checkbox is currently selected.

    Returns: - bool: true if selected

    Callbacks

    onCheckChanged

    The onCheckChanged callback is a std::function<void(bool)> that is triggered whenever the checkbox state changes via setChecked() or toggle().

    checkbox->onCheckChanged = [](bool isChecked) {
         Serial.println(isChecked ? "Checked!" : "Unchecked!");
     };
    -

    UICheckBox is designed to work seamlessly with UILayout containers (like UIVerticalLayout).

    • Focusable: Returns true for isFocusable(), allowing it to receive focus in a layout.
    • Input Handling: When selected (focused), it listens for the button index provided in the constructor (typically the 'A' button) to toggle its state.
    • Visual Feedback: When selected, it displays a selection indicator (usually a > character) if no background is drawn, or highlights its text/border.
    \ No newline at end of file +

    UICheckBox is designed to work seamlessly with UILayout containers (like UIVerticalLayout).

    • Focusable: Returns true for isFocusable(), allowing it to receive focus in a layout.
    • Input Handling: When selected (focused), it listens for the button index provided in the constructor (typically the 'A' button) to toggle its state.
    • Visual Feedback: When selected, it displays a selection indicator (usually a > character) if no background is drawn, or highlights its text/border.
    \ No newline at end of file diff --git a/site/api_reference/ui/ui_element/index.html b/site/api_reference/ui/ui_element/index.html index c262b80..31cdb7f 100644 --- a/site/api_reference/ui/ui_element/index.html +++ b/site/api_reference/ui/ui_element/index.html @@ -72,4 +72,4 @@ h = static_cast<float>(height); } }; -

    Performance Considerations

    • Render layer: UI elements are on layer 2, drawn after gameplay
    • Visibility: Use isVisible = false to hide elements efficiently
    • Layout integration: Layouts automatically manage element positioning

    ESP32 Considerations

    • Memory: Each UI element consumes memory (stay within MAX_ENTITIES)
    • Object pooling: Reuse UI elements when possible
    • Update frequency: Disable UI elements that don't need to update

    See Also

    \ No newline at end of file +

    Performance Considerations

    • Render layer: UI elements are on layer 2, drawn after gameplay
    • Visibility: Use isVisible = false to hide elements efficiently
    • Layout integration: Layouts automatically manage element positioning

    ESP32 Considerations

    • Memory: Each UI element consumes memory (stay within MAX_ENTITIES)
    • Object pooling: Reuse UI elements when possible
    • Update frequency: Disable UI elements that don't need to update

    See Also

    \ No newline at end of file diff --git a/site/api_reference/ui/ui_label/index.html b/site/api_reference/ui/ui_label/index.html index 1c3c3e0..c0181b4 100644 --- a/site/api_reference/ui/ui_label/index.html +++ b/site/api_reference/ui/ui_label/index.html @@ -96,4 +96,4 @@ ); title->centerX(128); // Center on screen addEntity(title); -

    Performance Considerations

    • Text updates: setText() recalculates size; avoid calling every frame if text doesn't change
    • String storage: Uses std::string; consider memory on ESP32
    • Rendering: Simple text drawing; very efficient
    • Static text: For static text, create once and don't update

    ESP32 Considerations

    • Memory: std::string uses heap memory; use static buffers when possible
    • Text updates: Limit frequency of text updates
    • String length: Keep text short to save memory

    See Also

    \ No newline at end of file +

    Performance Considerations

    • Text updates: setText() recalculates size; avoid calling every frame if text doesn't change
    • String storage: Uses std::string; consider memory on ESP32
    • Rendering: Simple text drawing; very efficient
    • Static text: For static text, create once and don't update

    ESP32 Considerations

    • Memory: std::string uses heap memory; use static buffers when possible
    • Text updates: Limit frequency of text updates
    • String length: Keep text short to save memory

    See Also

    \ No newline at end of file diff --git a/site/api_reference/ui/ui_layout/index.html b/site/api_reference/ui/ui_layout/index.html index b740a6c..3b6b5c7 100644 --- a/site/api_reference/ui/ui_layout/index.html +++ b/site/api_reference/ui/ui_layout/index.html @@ -3,4 +3,4 @@ // ... }; } -

    Inheritance

    • Inherits from: UIElement
    • Inherited by: UIVerticalLayout, UIHorizontalLayout, UIGridLayout, UIAnchorLayout

    ScrollBehavior Enum

    Defines how scrolling behaves in layouts.

    Values: - ScrollBehavior::NONE: No scrolling allowed - ScrollBehavior::SCROLL: Scroll freely within bounds - ScrollBehavior::CLAMP: Scroll but clamp to content bounds

    Public Methods

    virtual void addElement(UIElement* element) = 0

    Adds a UI element to the layout. Must be implemented by derived classes.

    Parameters: - element (UIElement*): Pointer to the element to add

    virtual void removeElement(UIElement* element) = 0

    Removes a UI element from the layout. Must be implemented by derived classes.

    Parameters: - element (UIElement*): Pointer to the element to remove

    virtual void updateLayout() = 0

    Recalculates positions of all elements in the layout. Must be implemented by derived classes.

    Returns: - void

    Notes: - Should be called automatically when elements are added/removed

    virtual void handleInput(const InputManager& input) = 0

    Handles input for layout navigation (scroll, selection, etc.). Must be implemented by derived classes.

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    void setPadding(float p)

    Sets the padding (internal spacing) of the layout.

    Parameters: - p (float): Padding value in pixels

    Returns: - void

    Notes: - Layout is automatically recalculated

    float getPadding() const

    Gets the current padding.

    Returns: - float: Padding value in pixels

    void setSpacing(float s)

    Sets the spacing between elements.

    Parameters: - s (float): Spacing value in pixels

    Returns: - void

    Notes: - Layout is automatically recalculated - Default: 4.0 pixels

    float getSpacing() const

    Gets the current spacing.

    Returns: - float: Spacing value in pixels

    size_t getElementCount() const

    Gets the number of elements in the layout.

    Returns: - size_t: Element count

    UIElement* getElement(size_t index) const

    Gets the element at a specific index.

    Parameters: - index (size_t): Element index

    Returns: - UIElement*: Pointer to the element, or nullptr if index is invalid

    void clearElements()

    Clears all elements from the layout.

    Returns: - void

    Notes: - Elements are not deleted (you must manage their lifetimes) - Layout is automatically recalculated

    Protected Members

    • std::vector<UIElement*> elements: List of child elements
    • float padding: Internal padding
    • float spacing: Spacing between elements (default: 4.0)
    • float scrollOffset: Current scroll offset
    • bool enableScroll: Whether scrolling is enabled
    • ScrollBehavior scrollBehavior: Scroll behavior mode

    See Also

    \ No newline at end of file +

    Inheritance

    • Inherits from: UIElement
    • Inherited by: UIVerticalLayout, UIHorizontalLayout, UIGridLayout, UIAnchorLayout

    ScrollBehavior Enum

    Defines how scrolling behaves in layouts.

    Values: - ScrollBehavior::NONE: No scrolling allowed - ScrollBehavior::SCROLL: Scroll freely within bounds - ScrollBehavior::CLAMP: Scroll but clamp to content bounds

    Public Methods

    virtual void addElement(UIElement* element) = 0

    Adds a UI element to the layout. Must be implemented by derived classes.

    Parameters: - element (UIElement*): Pointer to the element to add

    virtual void removeElement(UIElement* element) = 0

    Removes a UI element from the layout. Must be implemented by derived classes.

    Parameters: - element (UIElement*): Pointer to the element to remove

    virtual void updateLayout() = 0

    Recalculates positions of all elements in the layout. Must be implemented by derived classes.

    Returns: - void

    Notes: - Should be called automatically when elements are added/removed

    virtual void handleInput(const InputManager& input) = 0

    Handles input for layout navigation (scroll, selection, etc.). Must be implemented by derived classes.

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    void setPadding(float p)

    Sets the padding (internal spacing) of the layout.

    Parameters: - p (float): Padding value in pixels

    Returns: - void

    Notes: - Layout is automatically recalculated

    float getPadding() const

    Gets the current padding.

    Returns: - float: Padding value in pixels

    void setSpacing(float s)

    Sets the spacing between elements.

    Parameters: - s (float): Spacing value in pixels

    Returns: - void

    Notes: - Layout is automatically recalculated - Default: 4.0 pixels

    float getSpacing() const

    Gets the current spacing.

    Returns: - float: Spacing value in pixels

    size_t getElementCount() const

    Gets the number of elements in the layout.

    Returns: - size_t: Element count

    UIElement* getElement(size_t index) const

    Gets the element at a specific index.

    Parameters: - index (size_t): Element index

    Returns: - UIElement*: Pointer to the element, or nullptr if index is invalid

    void clearElements()

    Clears all elements from the layout.

    Returns: - void

    Notes: - Elements are not deleted (you must manage their lifetimes) - Layout is automatically recalculated

    Protected Members

    • std::vector<UIElement*> elements: List of child elements
    • float padding: Internal padding
    • float spacing: Spacing between elements (default: 4.0)
    • float scrollOffset: Current scroll offset
    • bool enableScroll: Whether scrolling is enabled
    • ScrollBehavior scrollBehavior: Scroll behavior mode

    See Also

    \ No newline at end of file diff --git a/site/api_reference/ui/ui_layouts/anchor_layout/index.html b/site/api_reference/ui/ui_layouts/anchor_layout/index.html index 9eed77e..44265a7 100644 --- a/site/api_reference/ui/ui_layouts/anchor_layout/index.html +++ b/site/api_reference/ui/ui_layouts/anchor_layout/index.html @@ -89,4 +89,4 @@ // HUD is drawn automatically (on layer 2) } }; -

    Anchor Positioning

    Elements are positioned based on their anchor:

    • TOP_LEFT: Element's top-left at screen top-left
    • TOP_RIGHT: Element's top-right at screen top-right
    • BOTTOM_LEFT: Element's bottom-left at screen bottom-left
    • BOTTOM_RIGHT: Element's bottom-right at screen bottom-right
    • CENTER: Element centered on screen
    • TOP_CENTER: Element centered horizontally, top-aligned
    • BOTTOM_CENTER: Element centered horizontally, bottom-aligned
    • LEFT_CENTER: Element centered vertically, left-aligned
    • RIGHT_CENTER: Element centered vertically, right-aligned

    Performance Considerations

    • No reflow: Very efficient (positions calculated once)
    • Fixed positions: Ideal for HUD elements
    • Viewport independent: Elements stay in fixed screen positions

    ESP32 Considerations

    • Memory: Very efficient (no complex calculations)
    • Update frequency: Positions only recalculate when screen size changes

    See Also

    \ No newline at end of file +

    Anchor Positioning

    Elements are positioned based on their anchor:

    • TOP_LEFT: Element's top-left at screen top-left
    • TOP_RIGHT: Element's top-right at screen top-right
    • BOTTOM_LEFT: Element's bottom-left at screen bottom-left
    • BOTTOM_RIGHT: Element's bottom-right at screen bottom-right
    • CENTER: Element centered on screen
    • TOP_CENTER: Element centered horizontally, top-aligned
    • BOTTOM_CENTER: Element centered horizontally, bottom-aligned
    • LEFT_CENTER: Element centered vertically, left-aligned
    • RIGHT_CENTER: Element centered vertically, right-aligned

    Performance Considerations

    • No reflow: Very efficient (positions calculated once)
    • Fixed positions: Ideal for HUD elements
    • Viewport independent: Elements stay in fixed screen positions

    ESP32 Considerations

    • Memory: Very efficient (no complex calculations)
    • Update frequency: Positions only recalculate when screen size changes

    See Also

    \ No newline at end of file diff --git a/site/api_reference/ui/ui_layouts/grid_layout/index.html b/site/api_reference/ui/ui_layouts/grid_layout/index.html index dbd9ae0..9a4b89e 100644 --- a/site/api_reference/ui/ui_layouts/grid_layout/index.html +++ b/site/api_reference/ui/ui_layouts/grid_layout/index.html @@ -39,4 +39,4 @@ addEntity(inventory); } }; -

    See Also

    \ No newline at end of file +

    See Also

    \ No newline at end of file diff --git a/site/api_reference/ui/ui_layouts/horizontal_layout/index.html b/site/api_reference/ui/ui_layouts/horizontal_layout/index.html index 13b2f68..b66c0f8 100644 --- a/site/api_reference/ui/ui_layouts/horizontal_layout/index.html +++ b/site/api_reference/ui/ui_layouts/horizontal_layout/index.html @@ -35,4 +35,4 @@ addEntity(toolbar); } }; -

    See Also

    \ No newline at end of file +

    See Also

    \ No newline at end of file diff --git a/site/api_reference/ui/ui_layouts/padding_container/index.html b/site/api_reference/ui/ui_layouts/padding_container/index.html index 6903341..936454b 100644 --- a/site/api_reference/ui/ui_layouts/padding_container/index.html +++ b/site/api_reference/ui/ui_layouts/padding_container/index.html @@ -42,4 +42,4 @@ auto* paddedLayout = new UIPaddingContainer(0, 0, 128, 128); paddedLayout->setPadding(10.0f, 10.0f, 20.0f, 20.0f); // Asymmetric paddedLayout->setChild(layout); -

    Performance Considerations

    • Rendering: Very efficient (just draws child)
    • Position calculation: Fast (simple addition)
    • Memory: Minimal overhead

    ESP32 Considerations

    • Memory: Very lightweight
    • Update frequency: Position only recalculates when padding/position changes

    See Also

    \ No newline at end of file +

    Performance Considerations

    • Rendering: Very efficient (just draws child)
    • Position calculation: Fast (simple addition)
    • Memory: Minimal overhead

    ESP32 Considerations

    • Memory: Very lightweight
    • Update frequency: Position only recalculates when padding/position changes

    See Also

    \ No newline at end of file diff --git a/site/api_reference/ui/ui_layouts/panel/index.html b/site/api_reference/ui/ui_layouts/panel/index.html index 117206b..341c25e 100644 --- a/site/api_reference/ui/ui_layouts/panel/index.html +++ b/site/api_reference/ui/ui_layouts/panel/index.html @@ -64,4 +64,4 @@ addEntity(dialog); } }; -

    Performance Considerations

    • Rendering: Simple rectangles; very efficient
    • Child updates: Child element updates are fast
    • Memory: Small overhead (just colors and border width)

    ESP32 Considerations

    • Memory: Panel is lightweight
    • Rendering: Two rectangles (background + border); minimal overhead

    See Also

    \ No newline at end of file +

    Performance Considerations

    • Rendering: Simple rectangles; very efficient
    • Child updates: Child element updates are fast
    • Memory: Small overhead (just colors and border width)

    ESP32 Considerations

    • Memory: Panel is lightweight
    • Rendering: Two rectangles (background + border); minimal overhead

    See Also

    \ No newline at end of file diff --git a/site/api_reference/ui/ui_layouts/vertical_layout/index.html b/site/api_reference/ui/ui_layouts/vertical_layout/index.html index fc67bdf..25e2c12 100644 --- a/site/api_reference/ui/ui_layouts/vertical_layout/index.html +++ b/site/api_reference/ui/ui_layouts/vertical_layout/index.html @@ -80,4 +80,4 @@ Scene::draw(renderer); // Draws layout and buttons } }; -

    The layout handles D-pad navigation automatically:

    • UP button: Moves selection up
    • DOWN button: Moves selection down
    • Action button: Triggers selected button's callback
    • Scrolling: Automatically scrolls to keep selected element visible

    Performance Considerations

    • Viewport culling: Only visible elements are drawn
    • Layout recalculation: Fast (simple positioning)
    • Scrolling: Smooth scrolling is efficient

    ESP32 Considerations

    • Element count: Stay within MAX_ENTITIES limit
    • Scrolling: Smooth scrolling uses minimal CPU

    See Also

    \ No newline at end of file +

    The layout handles D-pad navigation automatically:

    • UP button: Moves selection up
    • DOWN button: Moves selection down
    • Action button: Triggers selected button's callback
    • Scrolling: Automatically scrolls to keep selected element visible

    Performance Considerations

    • Viewport culling: Only visible elements are drawn
    • Layout recalculation: Fast (simple positioning)
    • Scrolling: Smooth scrolling is efficient

    ESP32 Considerations

    • Element count: Stay within MAX_ENTITIES limit
    • Scrolling: Smooth scrolling uses minimal CPU

    See Also

    \ No newline at end of file diff --git a/site/getting_started/fundamental_concepts/index.html b/site/getting_started/fundamental_concepts/index.html index 3a24eed..3924de4 100644 --- a/site/getting_started/fundamental_concepts/index.html +++ b/site/getting_started/fundamental_concepts/index.html @@ -9,4 +9,4 @@ 5. Detect collisions in the scene 6. Draw the scene (draw all visible entities) 7. Repeat -

    This cycle runs continuously, typically at 30-60 FPS on ESP32, or faster on PC.

    Update

    Each frame, all enabled entities receive a call to their update(deltaTime) method. This is where: - Entities move - Animations update - Game logic is processed - User input is read - Sound effects are played

    The deltaTime is passed in milliseconds and represents how much time has passed since the last frame. This allows movement to be framerate-independent.

    Rendering (Draw)

    After updating, all visible entities receive a call to their draw(renderer) method. This is where: - Sprites are drawn - Text is drawn - Primitives are drawn (rectangles, circles, etc.)

    The renderer is passed as a parameter so entities can draw themselves.

    Cleanup

    When you change scenes or end the game: - Entities from the previous scene can be cleaned up - Resources are freed - The new scene is initialized

    Conceptual Summary

    To summarize, PixelRoot32 works like this:

    1. Engine coordinates everything and runs the game loop
    2. Scene organizes your game into screens/levels
    3. Entity is any object in your game
    4. Actor is an entity that can collide
    5. PhysicsActor is an actor with automatic physics
    6. Renderer draws everything on screen using layers
    7. Each frame updates logic and then draws

    All of this works automatically once you configure the Engine and create your scenes and entities. You don't need to worry about game loop details; you just need to implement update() and draw() in your entities.

    Next Step

    Now that you understand the fundamental concepts, you're ready to create your first project and see these concepts in action with real code.


    See also: - What is PixelRoot32? - Why PixelRoot32? - Your First Project - Manual - Scenes and Entities

    \ No newline at end of file +

    This cycle runs continuously, typically at 30-60 FPS on ESP32, or faster on PC.

    Update

    Each frame, all enabled entities receive a call to their update(deltaTime) method. This is where: - Entities move - Animations update - Game logic is processed - User input is read - Sound effects are played

    The deltaTime is passed in milliseconds and represents how much time has passed since the last frame. This allows movement to be framerate-independent.

    Rendering (Draw)

    After updating, all visible entities receive a call to their draw(renderer) method. This is where: - Sprites are drawn - Text is drawn - Primitives are drawn (rectangles, circles, etc.)

    The renderer is passed as a parameter so entities can draw themselves.

    Cleanup

    When you change scenes or end the game: - Entities from the previous scene can be cleaned up - Resources are freed - The new scene is initialized

    Conceptual Summary

    To summarize, PixelRoot32 works like this:

    1. Engine coordinates everything and runs the game loop
    2. Scene organizes your game into screens/levels
    3. Entity is any object in your game
    4. Actor is an entity that can collide
    5. PhysicsActor is an actor with automatic physics
    6. Renderer draws everything on screen using layers
    7. Each frame updates logic and then draws

    All of this works automatically once you configure the Engine and create your scenes and entities. You don't need to worry about game loop details; you just need to implement update() and draw() in your entities.

    Next Step

    Now that you understand the fundamental concepts, you're ready to create your first project and see these concepts in action with real code.


    See also: - What is PixelRoot32? - Why PixelRoot32? - Your First Project - Manual - Scenes and Entities

    \ No newline at end of file diff --git a/site/getting_started/installation/index.html b/site/getting_started/installation/index.html index fa12c17..6b1027b 100644 --- a/site/getting_started/installation/index.html +++ b/site/getting_started/installation/index.html @@ -1,3 +1,3 @@ Installation - PixelRoot32 Documentation

    Installation

    This guide covers installing the PixelRoot32 documentation environment and preparing your development setup for ESP32 and Native (PC) targets.

    Requirements

    • Python 3.11 or newer
    • Git (recommended for source management)
    • VS Code (or your preferred IDE)
    • For ESP32 targets: PlatformIO (VS Code extension) with ESP32 toolchain
    • For Native targets: a C++ build toolchain (CMake or your OS-native toolchain)

    Install Documentation Tooling

    To build and preview this documentation locally:

    pip install mkdocs mkdocs-material mkdocs-minify-plugin mkdocs-git-revision-date-localized-plugin mike
     mkdocs serve
    -

    Open http://127.0.0.1:8000 in your browser to preview.

    1. Install VS Code
    2. Install PlatformIO IDE extension
    3. Install ESP32 platform/toolchain via PlatformIO
    4. Clone the engine repository:
    5. https://github.com/Gperez88/PixelRoot32-Game-Engine
    6. Open the engine or example project in VS Code (PlatformIO)
    7. Build and upload to your ESP32 board

    Tip: Use boards based on ESP32-WROOM/WROVER for best compatibility. Ensure a reliable USB cable and correct serial port selection.

    Native (PC) Setup

    1. Install a C++ toolchain (e.g., MSVC or MinGW on Windows)
    2. Install CMake (if the engine provides CMake build files)
    3. Clone the engine repository:
    4. https://github.com/Gperez88/PixelRoot32-Game-Engine
    5. Configure and build the native runtime:
    6. Follow the engine’s native build instructions (Development → Compiling)

    Verify Your Environment

    • ESP32: Build and flash a minimal sample; confirm serial output and display if applicable
    • Native: Run the executable; confirm window output and input handling

    Troubleshooting

    • If PlatformIO cannot find the ESP32 platform, update PlatformIO and retry
    • If native builds fail, verify compiler versions and CMake generator settings
    • Use Community → Troubleshooting for common issues and fixes

    Next Steps

    \ No newline at end of file +

    Open http://127.0.0.1:8000 in your browser to preview.

    1. Install VS Code
    2. Install PlatformIO IDE extension
    3. Install ESP32 platform/toolchain via PlatformIO
    4. Clone the engine repository:
    5. https://github.com/Gperez88/PixelRoot32-Game-Engine
    6. Open the engine or example project in VS Code (PlatformIO)
    7. Build and upload to your ESP32 board

    Tip: Use boards based on ESP32-WROOM/WROVER for best compatibility. Ensure a reliable USB cable and correct serial port selection.

    Native (PC) Setup

    1. Install a C++ toolchain (e.g., MSVC or MinGW on Windows)
    2. Install CMake (if the engine provides CMake build files)
    3. Clone the engine repository:
    4. https://github.com/Gperez88/PixelRoot32-Game-Engine
    5. Configure and build the native runtime:
    6. Follow the engine’s native build instructions (Development → Compiling)

    Verify Your Environment

    • ESP32: Build and flash a minimal sample; confirm serial output and display if applicable
    • Native: Run the executable; confirm window output and input handling

    Troubleshooting

    • If PlatformIO cannot find the ESP32 platform, update PlatformIO and retry
    • If native builds fail, verify compiler versions and CMake generator settings
    • Use Community → Troubleshooting for common issues and fixes

    Next Steps

    \ No newline at end of file diff --git a/site/getting_started/what_is_pixelroot32/index.html b/site/getting_started/what_is_pixelroot32/index.html index caabcb1..83f715c 100644 --- a/site/getting_started/what_is_pixelroot32/index.html +++ b/site/getting_started/what_is_pixelroot32/index.html @@ -1 +1 @@ - What is PixelRoot32? - PixelRoot32 Documentation

    What is PixelRoot32?

    PixelRoot32 is a lightweight, modular 2D game engine written in C++ designed specifically for ESP32 microcontrollers, with a native simulation layer for PC (SDL2) that allows you to develop and debug quickly on your desktop before deploying to hardware.

    Simple Definition

    PixelRoot32 is a game engine that lets you create retro-style 8-bit/16-bit video games directly on an ESP32 board, with the ability to develop and test on your PC before transferring code to hardware.

    Key Features

    🎮 Scene-Based Architecture

    • Scene system inspired by Godot Engine
    • Intuitive management of levels, menus, and screens
    • Simple transitions between scenes

    🎨 Optimized Rendering

    • 1bpp (monochrome) sprites as the standard format
    • Support for multi-layer sprites (MultiSprite)
    • Experimental 2bpp and 4bpp formats for higher fidelity
    • Retro color palette system (NES, GameBoy, PICO-8, etc.)
    • Compact tilemaps for backgrounds and levels
    • 2D camera with dead-zone for smooth scrolling
    • Render layer system (background, gameplay, UI)

    🔊 NES-like Audio

    • 4 audio channels (2 Pulse, 1 Triangle, 1 Noise)
    • Integrated sound effects system
    • Music player for background melodies
    • Backends for ESP32 (internal DAC or external I2S) and SDL2

    🎯 Physics and Collisions

    • AABB (Axis-Aligned Bounding Box) collision system
    • PhysicsActor with gravity, friction, and restitution
    • Collision layers and masks for fine control
    • World boundary collision detection

    🖥️ User Interface

    • Basic elements: Labels, Buttons, Panels
    • Automatic layouts: Vertical, Horizontal, Grid, Anchor
    • Integrated D-pad navigation
    • Scroll and viewport culling for long lists

    ⚡ Optimized for ESP32

    • Efficient memory management
    • Integrated object pooling
    • No dynamic allocations in the game loop
    • Performance optimized for limited hardware

    Typical Use Cases

    PixelRoot32 is ideal for creating:

    • Arcade Games: Space Invaders, Pong, Breakout
    • Platformers: Horizontal scrolling games with simple physics
    • Puzzles: Tetris, Snake, logic games
    • Simple RPGs: Basic role-playing games with tilemaps
    • Shooters: Vertical or horizontal shooting games
    • Rapid Prototypes: Quick development of game ideas

    Supported Platforms

    ESP32

    • Display: TFT_eSPI (ST7735, ILI9341, ST7789, etc.)
    • Audio: Internal DAC (GPIO 25/26) or external I2S (MAX98357A, PCM5102)
    • Input: Digital buttons, D-pad
    • Hardware: Any ESP32 board (ESP32-WROOM, ESP32-WROVER, etc.)

    Desktop/Native (PC)

    • Display: SDL2 (Windows, Linux, macOS)
    • Audio: SDL2 Audio
    • Input: Keyboard, mouse
    • Usage: Development, debugging, testing

    Note: Support for u8g2 (OLEDs) is planned for the future.

    Project Status

    Current Version: v0.2.0-dev

    PixelRoot32 is under active development. APIs may change and some subsystems are still experimental. Occasional changes or breaking changes are expected, especially on less-tested configurations.

    Stable Features

    • Scene and entity system
    • Basic rendering (1bpp sprites)
    • NES-like audio system
    • Basic physics and collisions
    • Basic UI system
    • ESP32 and Native support

    Experimental Features

    • 2bpp and 4bpp sprites (require compilation flags)
    • Scene Arena (advanced memory management)

    Planned Features

    • Support for u8g2 (OLEDs)
    • Music compiler
    • Tilemap compiler
    • Save/load system
    • Spatial partitioning for collisions

    Quick Comparison

    When to use PixelRoot32?

    ✅ Use PixelRoot32 if: - You want to create retro games on ESP32 - You need a lightweight and efficient engine - You prefer a simple and clear architecture - You want to develop on PC and deploy to ESP32 - You like 8-bit/16-bit style games

    ❌ Don't use PixelRoot32 if: - You need 3D graphics - You require advanced shaders - You need complex physics (advanced physics engines) - You want to create modern AAA games - You need support for multiple mobile platforms

    Next Step

    Now that you understand what PixelRoot32 is, discover why you should use it or go directly to your first project.


    See also: - Fundamental Concepts - Installation - API Reference

    \ No newline at end of file + What is PixelRoot32? - PixelRoot32 Documentation

    What is PixelRoot32?

    PixelRoot32 is a lightweight, modular 2D game engine written in C++ designed specifically for ESP32 microcontrollers, with a native simulation layer for PC (SDL2) that allows you to develop and debug quickly on your desktop before deploying to hardware.

    Simple Definition

    PixelRoot32 is a game engine that lets you create retro-style 8-bit/16-bit video games directly on an ESP32 board, with the ability to develop and test on your PC before transferring code to hardware.

    Key Features

    🎮 Scene-Based Architecture

    • Scene system inspired by Godot Engine
    • Intuitive management of levels, menus, and screens
    • Simple transitions between scenes

    🎨 Optimized Rendering

    • 1bpp (monochrome) sprites as the standard format
    • Support for multi-layer sprites (MultiSprite)
    • Experimental 2bpp and 4bpp formats for higher fidelity
    • Retro color palette system (NES, GameBoy, PICO-8, etc.)
    • Compact tilemaps for backgrounds and levels
    • 2D camera with dead-zone for smooth scrolling
    • Render layer system (background, gameplay, UI)

    🔊 NES-like Audio

    • 4 audio channels (2 Pulse, 1 Triangle, 1 Noise)
    • Integrated sound effects system
    • Music player for background melodies
    • Backends for ESP32 (internal DAC or external I2S) and SDL2

    🎯 Physics and Collisions

    • AABB (Axis-Aligned Bounding Box) collision system
    • PhysicsActor with gravity, friction, and restitution
    • Collision layers and masks for fine control
    • World boundary collision detection

    🖥️ User Interface

    • Basic elements: Labels, Buttons, Panels
    • Automatic layouts: Vertical, Horizontal, Grid, Anchor
    • Integrated D-pad navigation
    • Scroll and viewport culling for long lists

    ⚡ Optimized for ESP32

    • Efficient memory management
    • Integrated object pooling
    • No dynamic allocations in the game loop
    • Performance optimized for limited hardware

    Typical Use Cases

    PixelRoot32 is ideal for creating:

    • Arcade Games: Space Invaders, Pong, Breakout
    • Platformers: Horizontal scrolling games with simple physics
    • Puzzles: Tetris, Snake, logic games
    • Simple RPGs: Basic role-playing games with tilemaps
    • Shooters: Vertical or horizontal shooting games
    • Rapid Prototypes: Quick development of game ideas

    Supported Platforms

    ESP32

    • Display: TFT_eSPI (ST7735, ILI9341, ST7789, etc.)
    • Audio: Internal DAC (GPIO 25/26) or external I2S (MAX98357A, PCM5102)
    • Input: Digital buttons, D-pad
    • Hardware: Any ESP32 board (ESP32-WROOM, ESP32-WROVER, etc.)

    Desktop/Native (PC)

    • Display: SDL2 (Windows, Linux, macOS)
    • Audio: SDL2 Audio
    • Input: Keyboard, mouse
    • Usage: Development, debugging, testing

    Note: Support for u8g2 (OLEDs) is planned for the future.

    Project Status

    Current Version: v0.2.0-dev

    PixelRoot32 is under active development. APIs may change and some subsystems are still experimental. Occasional changes or breaking changes are expected, especially on less-tested configurations.

    Stable Features

    • Scene and entity system
    • Basic rendering (1bpp sprites)
    • NES-like audio system
    • Basic physics and collisions
    • Basic UI system
    • ESP32 and Native support

    Experimental Features

    • 2bpp and 4bpp sprites (require compilation flags)
    • Scene Arena (advanced memory management)

    Planned Features

    • Support for u8g2 (OLEDs)
    • Music compiler
    • Tilemap compiler
    • Save/load system
    • Spatial partitioning for collisions

    Quick Comparison

    When to use PixelRoot32?

    ✅ Use PixelRoot32 if: - You want to create retro games on ESP32 - You need a lightweight and efficient engine - You prefer a simple and clear architecture - You want to develop on PC and deploy to ESP32 - You like 8-bit/16-bit style games

    ❌ Don't use PixelRoot32 if: - You need 3D graphics - You require advanced shaders - You need complex physics (advanced physics engines) - You want to create modern AAA games - You need support for multiple mobile platforms

    Next Step

    Now that you understand what PixelRoot32 is, discover why you should use it or go directly to your first project.


    See also: - Fundamental Concepts - Installation - API Reference

    \ No newline at end of file diff --git a/site/getting_started/why_pixelroot32/index.html b/site/getting_started/why_pixelroot32/index.html index 572906f..1f753ff 100644 --- a/site/getting_started/why_pixelroot32/index.html +++ b/site/getting_started/why_pixelroot32/index.html @@ -1 +1 @@ - Why PixelRoot32? - PixelRoot32 Documentation

    Why PixelRoot32?

    PixelRoot32 is specifically designed to solve the unique challenges of creating video games on embedded hardware like the ESP32, while maintaining the simplicity and productivity of modern development.

    Main Advantages

    🎯 Optimized for ESP32

    Memory Efficient - 1bpp sprite system that minimizes RAM and Flash usage - Integrated object pooling to avoid memory fragmentation - Compact tilemaps that reuse sprites - No dynamic allocations in the game loop

    Performance Optimized - Rendering optimized for ESP32 limitations - Efficient render layer system - Viewport culling to reduce draw calls - Rendering pipeline designed for limited hardware

    Real Hardware - Direct support for common TFT displays (ST7735, ILI9341, ST7789) - Integrated audio (internal DAC or external I2S) - Simple pin and hardware configuration

    🖥️ Cross-Platform Development

    Develop on PC, Deploy to ESP32 - Same code works on PC (SDL2) and ESP32 - Fast debugging on desktop - Testing without hardware needed - Rapid development iteration

    Visual Consistency - Native bitmap font system (pixel-perfect) - Same rendering on PC and ESP32 - Consistent color palettes - No surprises when transferring to hardware

    🎨 Retro Palette System

    Authentic Style - Predefined palettes: NES, GameBoy, GameBoy Color, PICO-8 - Dual palette mode for visual contrasts - Custom palettes for unique styles - Automatic color resolution (RGB565)

    Easy to Use - Change palette with one line of code - Consistent visualization across all sprites - No need to manually convert assets

    🔊 Integrated Audio

    Complete NES-like System - 4 audio channels (2 Pulse, 1 Triangle, 1 Noise) - Simple sound effects to create - Integrated music system - Backends for different hardware configurations

    No External Dependencies - Software-generated audio - No heavy audio libraries required - Full control over sound - Deterministic and predictable

    🏗️ Simple and Clear Architecture

    Easy to Understand - Intuitive scene system (inspired by Godot) - Clear hierarchy: Entity → Actor → PhysicsActor - Consistent and predictable APIs - Clean and well-organized code

    Quick to Learn - Familiar concepts for game developers - Clear documentation and complete examples - Smooth learning curve - Active community and support

    🎮 Complete Features

    Everything Needed for Games - Rendering (sprites, tilemaps, primitives) - Audio (effects and music) - Physics (gravity, collisions, basic physics) - UI (layouts, buttons, navigation) - Input (buttons, keyboard) - Camera (scroll, parallax)

    No Bloat - Only the essentials, nothing more - No heavy dependencies - Small and maintainable codebase - Easy to understand and modify

    🛠️ Tools and Ecosystem

    Available Tools - Sprite Compiler to convert PNG to sprites - Complete game examples - Templates and starter code - Extensive documentation

    Community and Support - Active and developing project - Open source (MIT License) - Feedback and contributions welcome - Examples available

    Comparison with Alternatives

    vs. Full Engines (Unity, Godot, etc.)

    PixelRoot32 Advantages: - ✅ Much lighter (fits in ESP32) - ✅ No unnecessary overhead - ✅ Full control over code - ✅ Specifically optimized for limited hardware

    Disadvantages: - ❌ Fewer advanced features - ❌ No visual editor - ❌ Fewer resources and community

    vs. Writing Everything from Scratch

    PixelRoot32 Advantages: - ✅ Rendering system already implemented - ✅ Integrated and working audio - ✅ Physics and collisions ready - ✅ Complete UI system - ✅ Saves months of development

    Disadvantages: - ❌ Less control over internal implementation - ❌ You must learn the engine API

    vs. Other ESP32 Engines

    PixelRoot32 Advantages: - ✅ More modern and clear architecture - ✅ Better documentation - ✅ Unique palette system - ✅ Integrated NES-like audio - ✅ Real cross-platform development

    Ideal Use Cases

    PixelRoot32 is perfect for:

    1. Educational Projects
    2. Learn game development
    3. Understand engine architecture
    4. Student projects

    5. Rapid Prototypes

    6. Quickly validate game ideas
    7. Create demos and proof-of-concepts
    8. Test mechanics

    9. Retro Games

    10. 8-bit/16-bit style games
    11. Arcade games
    12. Games with retro aesthetics

    13. Hardware Projects

    14. Games on small displays
    15. DIY portable consoles
    16. Maker/retro projects

    17. C++ Learning

    18. Clean and well-structured code
    19. Good programming practices
    20. Real and functional examples

    Limitations to Consider

    To be honest, PixelRoot32 has limitations:

    • Limited Hardware: Designed for ESP32, not powerful PCs
    • Simple Graphics: No 3D, no advanced shaders
    • Basic Physics: Not a complete physics engine
    • Restricted Memory: MAX_ENTITIES = 32 per scene
    • In Development: Some features are experimental

    If you need advanced features or powerful hardware, consider other engines. But for retro games on ESP32, PixelRoot32 is an excellent choice.

    Conclusion

    PixelRoot32 combines:

    • Simplicity of use
    • Efficiency for limited hardware
    • Completeness of essential features
    • Clarity of architecture
    • Productivity in development

    If you want to create retro games on ESP32 without the complexity of large engines, PixelRoot32 is the right choice.

    Next Step

    Now that you understand why PixelRoot32 is a good option, learn the fundamental concepts or start directly with your first project.


    See also: - What is PixelRoot32? - Fundamental Concepts - Your First Project

    \ No newline at end of file + Why PixelRoot32? - PixelRoot32 Documentation

    Why PixelRoot32?

    PixelRoot32 is specifically designed to solve the unique challenges of creating video games on embedded hardware like the ESP32, while maintaining the simplicity and productivity of modern development.

    Main Advantages

    🎯 Optimized for ESP32

    Memory Efficient - 1bpp sprite system that minimizes RAM and Flash usage - Integrated object pooling to avoid memory fragmentation - Compact tilemaps that reuse sprites - No dynamic allocations in the game loop

    Performance Optimized - Rendering optimized for ESP32 limitations - Efficient render layer system - Viewport culling to reduce draw calls - Rendering pipeline designed for limited hardware

    Real Hardware - Direct support for common TFT displays (ST7735, ILI9341, ST7789) - Integrated audio (internal DAC or external I2S) - Simple pin and hardware configuration

    🖥️ Cross-Platform Development

    Develop on PC, Deploy to ESP32 - Same code works on PC (SDL2) and ESP32 - Fast debugging on desktop - Testing without hardware needed - Rapid development iteration

    Visual Consistency - Native bitmap font system (pixel-perfect) - Same rendering on PC and ESP32 - Consistent color palettes - No surprises when transferring to hardware

    🎨 Retro Palette System

    Authentic Style - Predefined palettes: NES, GameBoy, GameBoy Color, PICO-8 - Dual palette mode for visual contrasts - Custom palettes for unique styles - Automatic color resolution (RGB565)

    Easy to Use - Change palette with one line of code - Consistent visualization across all sprites - No need to manually convert assets

    🔊 Integrated Audio

    Complete NES-like System - 4 audio channels (2 Pulse, 1 Triangle, 1 Noise) - Simple sound effects to create - Integrated music system - Backends for different hardware configurations

    No External Dependencies - Software-generated audio - No heavy audio libraries required - Full control over sound - Deterministic and predictable

    🏗️ Simple and Clear Architecture

    Easy to Understand - Intuitive scene system (inspired by Godot) - Clear hierarchy: Entity → Actor → PhysicsActor - Consistent and predictable APIs - Clean and well-organized code

    Quick to Learn - Familiar concepts for game developers - Clear documentation and complete examples - Smooth learning curve - Active community and support

    🎮 Complete Features

    Everything Needed for Games - Rendering (sprites, tilemaps, primitives) - Audio (effects and music) - Physics (gravity, collisions, basic physics) - UI (layouts, buttons, navigation) - Input (buttons, keyboard) - Camera (scroll, parallax)

    No Bloat - Only the essentials, nothing more - No heavy dependencies - Small and maintainable codebase - Easy to understand and modify

    🛠️ Tools and Ecosystem

    Available Tools - Sprite Compiler to convert PNG to sprites - Complete game examples - Templates and starter code - Extensive documentation

    Community and Support - Active and developing project - Open source (MIT License) - Feedback and contributions welcome - Examples available

    Comparison with Alternatives

    vs. Full Engines (Unity, Godot, etc.)

    PixelRoot32 Advantages: - ✅ Much lighter (fits in ESP32) - ✅ No unnecessary overhead - ✅ Full control over code - ✅ Specifically optimized for limited hardware

    Disadvantages: - ❌ Fewer advanced features - ❌ No visual editor - ❌ Fewer resources and community

    vs. Writing Everything from Scratch

    PixelRoot32 Advantages: - ✅ Rendering system already implemented - ✅ Integrated and working audio - ✅ Physics and collisions ready - ✅ Complete UI system - ✅ Saves months of development

    Disadvantages: - ❌ Less control over internal implementation - ❌ You must learn the engine API

    vs. Other ESP32 Engines

    PixelRoot32 Advantages: - ✅ More modern and clear architecture - ✅ Better documentation - ✅ Unique palette system - ✅ Integrated NES-like audio - ✅ Real cross-platform development

    Ideal Use Cases

    PixelRoot32 is perfect for:

    1. Educational Projects
    2. Learn game development
    3. Understand engine architecture
    4. Student projects

    5. Rapid Prototypes

    6. Quickly validate game ideas
    7. Create demos and proof-of-concepts
    8. Test mechanics

    9. Retro Games

    10. 8-bit/16-bit style games
    11. Arcade games
    12. Games with retro aesthetics

    13. Hardware Projects

    14. Games on small displays
    15. DIY portable consoles
    16. Maker/retro projects

    17. C++ Learning

    18. Clean and well-structured code
    19. Good programming practices
    20. Real and functional examples

    Limitations to Consider

    To be honest, PixelRoot32 has limitations:

    • Limited Hardware: Designed for ESP32, not powerful PCs
    • Simple Graphics: No 3D, no advanced shaders
    • Basic Physics: Not a complete physics engine
    • Restricted Memory: MAX_ENTITIES = 32 per scene
    • In Development: Some features are experimental

    If you need advanced features or powerful hardware, consider other engines. But for retro games on ESP32, PixelRoot32 is an excellent choice.

    Conclusion

    PixelRoot32 combines:

    • Simplicity of use
    • Efficiency for limited hardware
    • Completeness of essential features
    • Clarity of architecture
    • Productivity in development

    If you want to create retro games on ESP32 without the complexity of large engines, PixelRoot32 is the right choice.

    Next Step

    Now that you understand why PixelRoot32 is a good option, learn the fundamental concepts or start directly with your first project.


    See also: - What is PixelRoot32? - Fundamental Concepts - Your First Project

    \ No newline at end of file diff --git a/site/getting_started/your_first_project/index.html b/site/getting_started/your_first_project/index.html index 2cb3b9e..c6bf7ec 100644 --- a/site/getting_started/your_first_project/index.html +++ b/site/getting_started/your_first_project/index.html @@ -226,4 +226,4 @@ -std=c++17 -lSDL2 -mconsole -

    Note: Adjust the SDL2 include and library paths for your system.

    Step 7: Build and Run

    For ESP32

    1. Connect your ESP32 via USB
    2. Select the environment: Click on the PlatformIO icon → Select env:esp32dev
    3. Build: Click the checkmark icon (✓) or press Ctrl+Alt+B
    4. Upload: Click the arrow icon (→) or press Ctrl+Alt+U
    5. Monitor: Click the plug icon to open serial monitor

    You should see "PixelRoot32 initialized!" in the serial monitor and your display should show a blue rectangle and text.

    For Native (PC)

    1. Select the environment: Click on the PlatformIO icon → Select env:native
    2. Build and Run: Click the play icon (▶) or press Ctrl+Alt+R

    A window should open showing your scene with a blue rectangle and "Hello PixelRoot32!" text.

    Step 8: Verify It Works

    If everything is set up correctly, you should see:

    • ESP32: Display shows a blue rectangle at (50, 50) and white text "Hello PixelRoot32!" at (20, 20)
    • Native: Window shows the same content

    If you see this, congratulations! Your first PixelRoot32 project is working.

    Troubleshooting

    ESP32 Issues

    Display is blank: - Check wiring connections - Verify pin numbers in platformio.ini match your hardware - Check SPI frequency (try lowering it) - Verify display type (ST7789 vs ST7735)

    Compilation errors: - Ensure library version is exactly 0.2.0-dev - Check that TFT_eSPI is installed - Verify all include paths are correct

    Upload fails: - Check USB cable connection - Try different USB port - Press BOOT button on ESP32 during upload - Check COM port in PlatformIO

    Native Issues

    SDL2 not found: - Verify SDL2 is installed - Check include/library paths in platformio.ini - On Windows, ensure MSYS2 paths are correct

    Window doesn't open: - Check console for error messages - Verify SDL2 is properly linked - Try running from terminal to see errors

    Next Steps

    Now that you have a working project, you can:

    1. Learn about Scenes and Entities: See how to create game objects
    2. Add Input: Make your scene respond to buttons
    3. Add Sprites: Draw custom graphics
    4. Add Audio: Play sounds and music

    Continue with the Development Guide to learn more.


    See also: - Fundamental Concepts - Installation - Manual - Scenes and Entities - API Reference

    \ No newline at end of file +

    Note: Adjust the SDL2 include and library paths for your system.

    Step 7: Build and Run

    For ESP32

    1. Connect your ESP32 via USB
    2. Select the environment: Click on the PlatformIO icon → Select env:esp32dev
    3. Build: Click the checkmark icon (✓) or press Ctrl+Alt+B
    4. Upload: Click the arrow icon (→) or press Ctrl+Alt+U
    5. Monitor: Click the plug icon to open serial monitor

    You should see "PixelRoot32 initialized!" in the serial monitor and your display should show a blue rectangle and text.

    For Native (PC)

    1. Select the environment: Click on the PlatformIO icon → Select env:native
    2. Build and Run: Click the play icon (▶) or press Ctrl+Alt+R

    A window should open showing your scene with a blue rectangle and "Hello PixelRoot32!" text.

    Step 8: Verify It Works

    If everything is set up correctly, you should see:

    • ESP32: Display shows a blue rectangle at (50, 50) and white text "Hello PixelRoot32!" at (20, 20)
    • Native: Window shows the same content

    If you see this, congratulations! Your first PixelRoot32 project is working.

    Troubleshooting

    ESP32 Issues

    Display is blank: - Check wiring connections - Verify pin numbers in platformio.ini match your hardware - Check SPI frequency (try lowering it) - Verify display type (ST7789 vs ST7735)

    Compilation errors: - Ensure library version is exactly 0.2.0-dev - Check that TFT_eSPI is installed - Verify all include paths are correct

    Upload fails: - Check USB cable connection - Try different USB port - Press BOOT button on ESP32 during upload - Check COM port in PlatformIO

    Native Issues

    SDL2 not found: - Verify SDL2 is installed - Check include/library paths in platformio.ini - On Windows, ensure MSYS2 paths are correct

    Window doesn't open: - Check console for error messages - Verify SDL2 is properly linked - Try running from terminal to see errors

    Next Steps

    Now that you have a working project, you can:

    1. Learn about Scenes and Entities: See how to create game objects
    2. Add Input: Make your scene respond to buttons
    3. Add Sprites: Draw custom graphics
    4. Add Audio: Play sounds and music

    Continue with the Development Guide to learn more.


    See also: - Fundamental Concepts - Installation - Manual - Scenes and Entities - API Reference

    \ No newline at end of file diff --git a/site/index.html b/site/index.html index ac4ec92..f4fb5d9 100644 --- a/site/index.html +++ b/site/index.html @@ -1 +1 @@ - PixelRoot32 Documentation

    PixelRoot32 Documentation

    PixelRoot32 is a lightweight 2D game engine designed for ESP32 and native desktop targets. This site provides official, versioned documentation with clear guides, conceptual explanations, API references, and complete examples to help you build games efficiently.

    Getting Started

    New to PixelRoot32? Follow this learning path:

    1. What is PixelRoot32? - Understand what the engine is and what it can do
    2. Why PixelRoot32? - Learn the advantages and use cases
    3. Fundamental Concepts - Learn the core architecture concepts
    4. Your First Project - Create and run your first project

    About This Documentation

    • Professional technical English across all pages
    • Search-enabled, mobile-friendly UI
    • Versioned with mike (stable/dev/experimental)
    • Cross-linked concepts, API, and examples
    • Progressive learning path from basics to advanced topics
    \ No newline at end of file + PixelRoot32 Documentation

    PixelRoot32 Documentation

    PixelRoot32 is a lightweight 2D game engine designed for ESP32 and native desktop targets. This site provides official, versioned documentation with clear guides, conceptual explanations, API references, and complete examples to help you build games efficiently.

    Getting Started

    New to PixelRoot32? Follow this learning path:

    1. What is PixelRoot32? - Understand what the engine is and what it can do
    2. Why PixelRoot32? - Learn the advantages and use cases
    3. Fundamental Concepts - Learn the core architecture concepts
    4. Your First Project - Create and run your first project

    About This Documentation

    • Professional technical English across all pages
    • Search-enabled, mobile-friendly UI
    • Versioned with mike (stable/dev/experimental)
    • Cross-linked concepts, API, and examples
    • Progressive learning path from basics to advanced topics
    \ No newline at end of file diff --git a/site/manual/advanced_graphics/cameras_and_scrolling/index.html b/site/manual/advanced_graphics/cameras_and_scrolling/index.html index 2a9dba9..3c2487b 100644 --- a/site/manual/advanced_graphics/cameras_and_scrolling/index.html +++ b/site/manual/advanced_graphics/cameras_and_scrolling/index.html @@ -316,4 +316,4 @@ y + height < cameraY || y > cameraY + screenHeight); } -

    Troubleshooting

    Camera Not Moving

    • Verify camera.apply() is called in draw()
    • Check followTarget() or setPosition() is called in update()
    • Ensure camera is created with correct viewport size
    • Check boundaries aren't preventing movement

    Objects Not Visible

    • Verify objects are within camera view
    • Check world coordinates vs screen coordinates
    • Ensure camera is applied before drawing
    • Verify render layers are correct

    Parallax Not Working

    • Check setDisplayOffset() is used correctly
    • Verify parallax speed values (0.0 to 1.0)
    • Ensure offset is reset after parallax layers
    • Test with different speed values

    Next Steps

    Now that you understand cameras and scrolling, learn about: - Tilemaps - Build levels with tiles - Particles and Effects - Add visual effects - Performance Optimization - Optimize your game


    See also: - API Reference - Camera2D - Manual - Basic Rendering - Manual - Tilemaps

    \ No newline at end of file +

    Troubleshooting

    Camera Not Moving

    • Verify camera.apply() is called in draw()
    • Check followTarget() or setPosition() is called in update()
    • Ensure camera is created with correct viewport size
    • Check boundaries aren't preventing movement

    Objects Not Visible

    • Verify objects are within camera view
    • Check world coordinates vs screen coordinates
    • Ensure camera is applied before drawing
    • Verify render layers are correct

    Parallax Not Working

    • Check setDisplayOffset() is used correctly
    • Verify parallax speed values (0.0 to 1.0)
    • Ensure offset is reset after parallax layers
    • Test with different speed values

    Next Steps

    Now that you understand cameras and scrolling, learn about: - Tilemaps - Build levels with tiles - Particles and Effects - Add visual effects - Performance Optimization - Optimize your game


    See also: - API Reference - Camera2D - Manual - Basic Rendering - Manual - Tilemaps

    \ No newline at end of file diff --git a/site/manual/advanced_graphics/color_palettes/index.html b/site/manual/advanced_graphics/color_palettes/index.html index 640a043..ff19ec6 100644 --- a/site/manual/advanced_graphics/color_palettes/index.html +++ b/site/manual/advanced_graphics/color_palettes/index.html @@ -204,4 +204,4 @@ Color::White }; } -

    Troubleshooting

    Colors Not Changing

    • Verify setPalette() is called before drawing
    • Check palette is set in init(), not update()
    • Ensure dual palette mode is enabled if using separate palettes
    • Verify Color constants are from correct namespace

    Colors Look Wrong on Hardware

    • ESP32 displays may render colors differently
    • Test on actual hardware, not just PC
    • Adjust palette colors if needed
    • Consider display calibration

    Dual Palette Not Working

    • Ensure enableDualPaletteMode() is called first
    • Verify both palettes are set
    • Check that you're drawing in correct context
    • Review renderer documentation

    Next Steps

    Now that you understand palettes, learn about: - Cameras and Scrolling - Create scrolling levels - Tilemaps - Build levels with tiles - Particles and Effects - Add visual effects


    See also: - API Reference - PaletteDefs - API Reference - Color - Manual - Basic Rendering

    \ No newline at end of file +

    Troubleshooting

    Colors Not Changing

    • Verify setPalette() is called before drawing
    • Check palette is set in init(), not update()
    • Ensure dual palette mode is enabled if using separate palettes
    • Verify Color constants are from correct namespace

    Colors Look Wrong on Hardware

    • ESP32 displays may render colors differently
    • Test on actual hardware, not just PC
    • Adjust palette colors if needed
    • Consider display calibration

    Dual Palette Not Working

    • Ensure enableDualPaletteMode() is called first
    • Verify both palettes are set
    • Check that you're drawing in correct context
    • Review renderer documentation

    Next Steps

    Now that you understand palettes, learn about: - Cameras and Scrolling - Create scrolling levels - Tilemaps - Build levels with tiles - Particles and Effects - Add visual effects


    See also: - API Reference - PaletteDefs - API Reference - Color - Manual - Basic Rendering

    \ No newline at end of file diff --git a/site/manual/advanced_graphics/particles_and_effects/index.html b/site/manual/advanced_graphics/particles_and_effects/index.html index 8552c06..183ce08 100644 --- a/site/manual/advanced_graphics/particles_and_effects/index.html +++ b/site/manual/advanced_graphics/particles_and_effects/index.html @@ -287,4 +287,4 @@ emitter->update(deltaTime); } }; -

    Troubleshooting

    Particles Not Appearing

    • Verify emitter is added to scene
    • Check particle config is valid
    • Ensure burst() is being called
    • Verify emitter position is on-screen

    Performance Issues

    • Reduce particle count per burst
    • Limit number of active emitters
    • Use simpler particle configs
    • Disable emitters when not visible

    Particles Not Moving

    • Check gravity value (positive = down, negative = up)
    • Verify speed is not 0
    • Check friction isn't too high (1.0 = no movement)
    • Ensure direction is correct (degrees: 0=right, 90=up, 180=left, 270=down)

    Next Steps

    Now that you understand particles, you've completed the advanced graphics section. Continue with: - Performance Optimization - Optimize your game - Memory Management - Manage memory efficiently - API Reference - Complete API documentation


    See also: - API Reference - ParticleEmitter - API Reference - ParticleConfig - API Reference - ParticlePresets - Manual - Basic Rendering

    \ No newline at end of file +

    Troubleshooting

    Particles Not Appearing

    • Verify emitter is added to scene
    • Check particle config is valid
    • Ensure burst() is being called
    • Verify emitter position is on-screen

    Performance Issues

    • Reduce particle count per burst
    • Limit number of active emitters
    • Use simpler particle configs
    • Disable emitters when not visible

    Particles Not Moving

    • Check gravity value (positive = down, negative = up)
    • Verify speed is not 0
    • Check friction isn't too high (1.0 = no movement)
    • Ensure direction is correct (degrees: 0=right, 90=up, 180=left, 270=down)

    Next Steps

    Now that you understand particles, you've completed the advanced graphics section. Continue with: - Performance Optimization - Optimize your game - Memory Management - Manage memory efficiently - API Reference - Complete API documentation


    See also: - API Reference - ParticleEmitter - API Reference - ParticleConfig - API Reference - ParticlePresets - Manual - Basic Rendering

    \ No newline at end of file diff --git a/site/manual/advanced_graphics/sprites_and_animation/index.html b/site/manual/advanced_graphics/sprites_and_animation/index.html index f306e2e..7b4162c 100644 --- a/site/manual/advanced_graphics/sprites_and_animation/index.html +++ b/site/manual/advanced_graphics/sprites_and_animation/index.html @@ -350,4 +350,4 @@ return nullptr; } }; -

    Troubleshooting

    Sprites Not Displaying

    • Check sprite data is valid
    • Verify width/height match data
    • Ensure sprite is within screen bounds
    • Check render layer is correct

    Animation Not Playing

    • Verify animation frames are set
    • Check step() is being called
    • Ensure timer logic is correct
    • Verify frame count matches array size

    Memory Issues

    • Reduce sprite count
    • Use 1bpp instead of 2bpp/4bpp
    • Reuse sprites more
    • Check available flash memory

    Next Steps

    Now that you understand sprites and animation, learn about: - Color Palettes - Use different color schemes - Cameras and Scrolling - Create scrolling levels - Tilemaps - Build levels with tiles


    See also: - API Reference - Sprite - API Reference - SpriteAnimation - Manual - Basic Rendering

    \ No newline at end of file +

    Troubleshooting

    Sprites Not Displaying

    • Check sprite data is valid
    • Verify width/height match data
    • Ensure sprite is within screen bounds
    • Check render layer is correct

    Animation Not Playing

    • Verify animation frames are set
    • Check step() is being called
    • Ensure timer logic is correct
    • Verify frame count matches array size

    Memory Issues

    • Reduce sprite count
    • Use 1bpp instead of 2bpp/4bpp
    • Reuse sprites more
    • Check available flash memory

    Next Steps

    Now that you understand sprites and animation, learn about: - Color Palettes - Use different color schemes - Cameras and Scrolling - Create scrolling levels - Tilemaps - Build levels with tiles


    See also: - API Reference - Sprite - API Reference - SpriteAnimation - Manual - Basic Rendering

    \ No newline at end of file diff --git a/site/manual/advanced_graphics/tilemaps/index.html b/site/manual/advanced_graphics/tilemaps/index.html index 7fc2e3e..5c6a369 100644 --- a/site/manual/advanced_graphics/tilemaps/index.html +++ b/site/manual/advanced_graphics/tilemaps/index.html @@ -297,4 +297,4 @@ return false; // No collision } -

    Troubleshooting

    Tiles Not Appearing

    • Verify tile indices are correct (0 = first tile, 1 = second, etc.)
    • Check tilemap dimensions match indices array size
    • Ensure tiles array has enough entries
    • Verify tile size matches sprite size

    Performance Issues

    • Reduce tilemap size
    • Use smaller tiles
    • Limit number of unique tiles
    • Test viewport culling

    Scrolling Problems

    • Ensure camera is applied before drawing tilemap
    • Check tilemap position matches camera offset
    • Verify tilemap boundaries are correct
    • Test with simple tilemap first

    Next Steps

    Now that you understand tilemaps, learn about: - Particles and Effects - Add visual effects - Cameras and Scrolling - Combine with scrolling - Performance Optimization - Optimize rendering


    See also: - API Reference - TileMap - API Reference - Renderer - Manual - Cameras and Scrolling

    \ No newline at end of file +

    Troubleshooting

    Tiles Not Appearing

    • Verify tile indices are correct (0 = first tile, 1 = second, etc.)
    • Check tilemap dimensions match indices array size
    • Ensure tiles array has enough entries
    • Verify tile size matches sprite size

    Performance Issues

    • Reduce tilemap size
    • Use smaller tiles
    • Limit number of unique tiles
    • Test viewport culling

    Scrolling Problems

    • Ensure camera is applied before drawing tilemap
    • Check tilemap position matches camera offset
    • Verify tilemap boundaries are correct
    • Test with simple tilemap first

    Next Steps

    Now that you understand tilemaps, learn about: - Particles and Effects - Add visual effects - Cameras and Scrolling - Combine with scrolling - Performance Optimization - Optimize rendering


    See also: - API Reference - TileMap - API Reference - Renderer - Manual - Cameras and Scrolling

    \ No newline at end of file diff --git a/site/manual/game_development/audio/index.html b/site/manual/game_development/audio/index.html index 7e86b21..9c89916 100644 --- a/site/manual/game_development/audio/index.html +++ b/site/manual/game_development/audio/index.html @@ -234,4 +234,4 @@ Scene::update(deltaTime); } }; -

    Troubleshooting

    No Sound

    • Check audio backend is configured correctly
    • Verify sample rate matches backend
    • Check master volume is not 0
    • Ensure audio is initialized (engine.init())

    Distorted Sound

    • Lower volume levels
    • Reduce sample rate (ESP32 DAC works better at 11025 Hz)
    • Check for too many simultaneous sounds
    • Verify hardware connections (ESP32)

    Music Not Playing

    • Check music.isPlaying() status
    • Ensure track is properly defined
    • Verify MusicPlayer is updated (happens automatically)
    • Check that music channel is not being used by SFX

    Next Steps

    Now that you can add audio, learn about: - NES Audio Reference - Advanced audio techniques - Physics and Collisions - Make objects interact - User Interface - Create menus and HUDs


    See also: - API Reference - AudioEngine - API Reference - MusicPlayer - API Reference - Audio Types - Manual - Audio Overview

    \ No newline at end of file +

    Troubleshooting

    No Sound

    • Check audio backend is configured correctly
    • Verify sample rate matches backend
    • Check master volume is not 0
    • Ensure audio is initialized (engine.init())

    Distorted Sound

    • Lower volume levels
    • Reduce sample rate (ESP32 DAC works better at 11025 Hz)
    • Check for too many simultaneous sounds
    • Verify hardware connections (ESP32)

    Music Not Playing

    • Check music.isPlaying() status
    • Ensure track is properly defined
    • Verify MusicPlayer is updated (happens automatically)
    • Check that music channel is not being used by SFX

    Next Steps

    Now that you can add audio, learn about: - NES Audio Reference - Advanced audio techniques - Physics and Collisions - Make objects interact - User Interface - Create menus and HUDs


    See also: - API Reference - AudioEngine - API Reference - MusicPlayer - API Reference - Audio Types - Manual - Audio Overview

    \ No newline at end of file diff --git a/site/manual/game_development/basic_rendering/index.html b/site/manual/game_development/basic_rendering/index.html index 20a801a..e468a2f 100644 --- a/site/manual/game_development/basic_rendering/index.html +++ b/site/manual/game_development/basic_rendering/index.html @@ -140,4 +140,4 @@ renderer.drawSprite(sprite, x, startY, Color::White); } } -

    Next Steps

    Now that you can draw basic graphics, learn about: - Sprites and Animation - Advanced sprite techniques - Input and Control - Make your game interactive - Palettes - Use different color schemes


    See also: - API Reference - Renderer - API Reference - Sprite - Render Layers

    \ No newline at end of file +

    Next Steps

    Now that you can draw basic graphics, learn about: - Sprites and Animation - Advanced sprite techniques - Input and Control - Make your game interactive - Palettes - Use different color schemes


    See also: - API Reference - Renderer - API Reference - Sprite - Render Layers

    \ No newline at end of file diff --git a/site/manual/game_development/input_and_control/index.html b/site/manual/game_development/input_and_control/index.html index f864c2f..35ddf8e 100644 --- a/site/manual/game_development/input_and_control/index.html +++ b/site/manual/game_development/input_and_control/index.html @@ -258,4 +258,4 @@ if (input.isButtonReleased(button)) return InputState::RELEASED; return InputState::IDLE; } -

    Troubleshooting

    Button Not Responding

    • Check button indices match your InputConfig
    • Verify GPIO pins (ESP32) or scancodes (Native) are correct
    • Ensure InputManager is being updated (happens automatically in Engine)

    Input Feels Laggy

    • Ensure you're using deltaTime for movement
    • Check that input is read in update(), not draw()
    • Verify framerate is stable

    Multiple Triggers

    • Use isButtonPressed() instead of isButtonDown() for one-time actions
    • Implement input buffering or cooldown timers

    Next Steps

    Now that you can handle input, learn about: - Audio - Add sound effects and music - Physics and Collisions - Make objects interact - User Interface - Create menus and HUDs


    See also: - API Reference - InputManager - API Reference - InputConfig - Manual - Input Overview

    \ No newline at end of file +

    Troubleshooting

    Button Not Responding

    • Check button indices match your InputConfig
    • Verify GPIO pins (ESP32) or scancodes (Native) are correct
    • Ensure InputManager is being updated (happens automatically in Engine)

    Input Feels Laggy

    • Ensure you're using deltaTime for movement
    • Check that input is read in update(), not draw()
    • Verify framerate is stable

    Multiple Triggers

    • Use isButtonPressed() instead of isButtonDown() for one-time actions
    • Implement input buffering or cooldown timers

    Next Steps

    Now that you can handle input, learn about: - Audio - Add sound effects and music - Physics and Collisions - Make objects interact - User Interface - Create menus and HUDs


    See also: - API Reference - InputManager - API Reference - InputConfig - Manual - Input Overview

    \ No newline at end of file diff --git a/site/manual/game_development/physics_and_collisions/index.html b/site/manual/game_development/physics_and_collisions/index.html index cdc7def..32d1a15 100644 --- a/site/manual/game_development/physics_and_collisions/index.html +++ b/site/manual/game_development/physics_and_collisions/index.html @@ -323,4 +323,4 @@ } } } -

    Troubleshooting

    Collisions Not Detected

    • Verify collision layers and masks overlap
    • Check that actors are added to the scene
    • Ensure Scene::update() is called
    • Verify hitboxes are correct

    Physics Too Fast/Slow

    • Adjust values based on deltaTime
    • Check gravity and velocity values
    • Test on actual hardware (ESP32 may run slower)

    Objects Passing Through

    • Use sweep tests for fast objects
    • Increase collision detection frequency
    • Check hitbox sizes match visual size

    Next Steps

    Now that you understand physics and collisions, learn about: - User Interface - Create menus and HUDs - Advanced Graphics - Advanced sprite techniques - Camera and Scrolling - Create scrolling levels


    See also: - API Reference - PhysicsActor - API Reference - CollisionSystem - Manual - Physics Overview - Manual - Collision Detection

    \ No newline at end of file +

    Troubleshooting

    Collisions Not Detected

    • Verify collision layers and masks overlap
    • Check that actors are added to the scene
    • Ensure Scene::update() is called
    • Verify hitboxes are correct

    Physics Too Fast/Slow

    • Adjust values based on deltaTime
    • Check gravity and velocity values
    • Test on actual hardware (ESP32 may run slower)

    Objects Passing Through

    • Use sweep tests for fast objects
    • Increase collision detection frequency
    • Check hitbox sizes match visual size

    Next Steps

    Now that you understand physics and collisions, learn about: - User Interface - Create menus and HUDs - Advanced Graphics - Advanced sprite techniques - Camera and Scrolling - Create scrolling levels


    See also: - API Reference - PhysicsActor - API Reference - CollisionSystem - Manual - Physics Overview - Manual - Collision Detection

    \ No newline at end of file diff --git a/site/manual/game_development/scenes_and_entities/index.html b/site/manual/game_development/scenes_and_entities/index.html index 32ba43e..dc11d02 100644 --- a/site/manual/game_development/scenes_and_entities/index.html +++ b/site/manual/game_development/scenes_and_entities/index.html @@ -254,4 +254,4 @@ // ... } } -

    Next Steps

    Now that you understand scenes and entities, learn about: - Basic Rendering - Draw sprites, text, and primitives - Input and Control - Handle user input - Physics and Collisions - Advanced collision handling


    See also: - Fundamental Concepts - API Reference - Scene - API Reference - Entity - API Reference - Actor

    \ No newline at end of file +

    Next Steps

    Now that you understand scenes and entities, learn about: - Basic Rendering - Draw sprites, text, and primitives - Input and Control - Handle user input - Physics and Collisions - Advanced collision handling


    See also: - Fundamental Concepts - API Reference - Scene - API Reference - Entity - API Reference - Actor

    \ No newline at end of file diff --git a/site/manual/game_development/user_interface/index.html b/site/manual/game_development/user_interface/index.html index 4aff183..7417235 100644 --- a/site/manual/game_development/user_interface/index.html +++ b/site/manual/game_development/user_interface/index.html @@ -389,4 +389,4 @@ snprintf(buffer, sizeof(buffer), "Health: %d%%", health); healthLabel->setText(buffer); } -

    Next Steps

    Now that you understand the UI system, you've completed the core game development topics. Continue with: - Advanced Graphics - Advanced sprite techniques - Camera and Scrolling - Create scrolling levels - Performance Tuning - Improve performance


    See also: - API Reference - UIElement - API Reference - UIButton - API Reference - UI Layouts - Manual - UI Overview

    \ No newline at end of file +

    Next Steps

    Now that you understand the UI system, you've completed the core game development topics. Continue with: - Advanced Graphics - Advanced sprite techniques - Camera and Scrolling - Create scrolling levels - Performance Tuning - Improve performance


    See also: - API Reference - UIElement - API Reference - UIButton - API Reference - UI Layouts - Manual - UI Overview

    \ No newline at end of file diff --git a/site/manual/optimization/extensibility/index.html b/site/manual/optimization/extensibility/index.html index 06e9166..55dfa82 100644 --- a/site/manual/optimization/extensibility/index.html +++ b/site/manual/optimization/extensibility/index.html @@ -348,4 +348,4 @@ } } }; -

    Troubleshooting

    Driver Not Working

    • Verify all interface methods are implemented
    • Check initialization order
    • Test with simple drawing first
    • Verify hardware connections

    Audio Backend Issues

    • Check sample rate matches hardware
    • Verify audio generation logic
    • Test with simple tones first
    • Check channel management

    Extension Conflicts

    • Ensure namespace isolation
    • Avoid modifying engine code directly
    • Use composition over modification
    • Test with engine updates

    Next Steps

    Now that you understand extensibility, you've completed the optimization section. Continue with: - API Reference - Complete API documentation - Examples - Code examples - Resources - Tools and troubleshooting


    See also: - API Reference - DrawSurface - API Reference - AudioBackend - Manual - Platforms and Drivers

    \ No newline at end of file +

    Troubleshooting

    Driver Not Working

    • Verify all interface methods are implemented
    • Check initialization order
    • Test with simple drawing first
    • Verify hardware connections

    Audio Backend Issues

    • Check sample rate matches hardware
    • Verify audio generation logic
    • Test with simple tones first
    • Check channel management

    Extension Conflicts

    • Ensure namespace isolation
    • Avoid modifying engine code directly
    • Use composition over modification
    • Test with engine updates

    Next Steps

    Now that you understand extensibility, you've completed the optimization section. Continue with: - API Reference - Complete API documentation - Examples - Code examples - Resources - Tools and troubleshooting


    See also: - API Reference - DrawSurface - API Reference - AudioBackend - Manual - Platforms and Drivers

    \ No newline at end of file diff --git a/site/manual/optimization/memory_management/index.html b/site/manual/optimization/memory_management/index.html index 8d91845..0623234 100644 --- a/site/manual/optimization/memory_management/index.html +++ b/site/manual/optimization/memory_management/index.html @@ -294,4 +294,4 @@ int size() const { return count; } pixelroot32::core::Entity* operator[](int index) { return entities[index]; } }; -

    Troubleshooting

    Out of Memory Errors

    • Reduce pool sizes
    • Use fewer entities
    • Store more data in flash
    • Avoid dynamic allocation
    • Check for memory leaks

    Entity Limit Reached

    • MAX_ENTITIES = 32 is a hard limit
    • Use object pooling to reuse entities
    • Deactivate entities instead of removing
    • Combine multiple entities into one

    Memory Fragmentation

    • Use object pooling
    • Pre-allocate all resources
    • Avoid frequent new/delete
    • Consider Scene Arena (experimental)

    Next Steps

    Now that you understand memory management, learn about: - Performance Optimization - Improve game performance - Platforms and Drivers - Understand platform specifics - Extensibility - Extend the engine


    See also: - API Reference - Scene - Manual - Scenes and Entities

    \ No newline at end of file +

    Troubleshooting

    Out of Memory Errors

    • Reduce pool sizes
    • Use fewer entities
    • Store more data in flash
    • Avoid dynamic allocation
    • Check for memory leaks

    Entity Limit Reached

    • MAX_ENTITIES = 32 is a hard limit
    • Use object pooling to reuse entities
    • Deactivate entities instead of removing
    • Combine multiple entities into one

    Memory Fragmentation

    • Use object pooling
    • Pre-allocate all resources
    • Avoid frequent new/delete
    • Consider Scene Arena (experimental)

    Next Steps

    Now that you understand memory management, learn about: - Performance Optimization - Improve game performance - Platforms and Drivers - Understand platform specifics - Extensibility - Extend the engine


    See also: - API Reference - Scene - Manual - Scenes and Entities

    \ No newline at end of file diff --git a/site/manual/optimization/performance_tuning/index.html b/site/manual/optimization/performance_tuning/index.html index fb2245e..6a9349c 100644 --- a/site/manual/optimization/performance_tuning/index.html +++ b/site/manual/optimization/performance_tuning/index.html @@ -16,7 +16,7 @@ if (levelMap.data[tileY * levelMap.width + tileX] != 0) { // Colisión detectada } -

    Recomendaciones Generales

    • Sprites Indexados: Prefiere Sprite2bpp (4 colores) o Sprite4bpp (16 colores) sobre Sprite (1bpp) si necesitas color, ya que están altamente optimizados.
    • Evitar std::string en el Loop: Las concatenaciones de strings generan fragmentación de memoria. Usa buffers estáticos o char[] para textos dinámicos.
    • Perfilado: Utiliza engine.getFPS() para monitorear el impacto de tus cambios en tiempo real.

    Common Optimization Patterns

    Update Frequency Reduction

    class LowFrequencyUpdater {
    +

    Recomendaciones Generales

    • Sprites Indexados: Prefiere Sprite2bpp (4 colores) o Sprite4bpp (16 colores) sobre Sprite (1bpp) si necesitas color, ya que están altamente optimizados.
    • Evitar std::string en el Loop: Las concatenaciones de strings generan fragmentación de memoria. Usa buffers estáticos o char[] para textos dinámicos.
    • Perfilado: Activa el overlay de FPS compilando con PIXELROOT32_ENABLE_FPS_DISPLAY (ver Engine - FPS overlay) para monitorear el impacto de tus cambios en tiempo real.

    Common Optimization Patterns

    Update Frequency Reduction

    class LowFrequencyUpdater {
     private:
         unsigned long timer = 0;
         unsigned long interval = 100; // Update every 100ms
    @@ -64,4 +64,4 @@
             }
         }
     };
    -

    Troubleshooting

    Low FPS

    • Profile to find bottlenecks
    • Reduce entity count
    • Optimize rendering (culling, batching)
    • Simplify collision detection
    • Reduce update frequency

    Frame Drops

    • Check for expensive operations in update()
    • Avoid dynamic allocation
    • Cache calculations
    • Reduce draw calls

    Stuttering

    • Ensure frame-rate independence (use deltaTime)
    • Avoid blocking operations
    • Pre-load resources
    • Use object pooling

    Next Steps

    Now that you understand performance optimization, learn about: - Memory Management - Manage memory efficiently - Platforms and Drivers - Platform-specific optimizations - Extensibility - Extend the engine


    See also: - Manual - Basic Rendering - Manual - Physics and Collisions

    \ No newline at end of file +

    Troubleshooting

    Low FPS

    • Profile to find bottlenecks
    • Reduce entity count
    • Optimize rendering (culling, batching)
    • Simplify collision detection
    • Reduce update frequency

    Frame Drops

    • Check for expensive operations in update()
    • Avoid dynamic allocation
    • Cache calculations
    • Reduce draw calls

    Stuttering

    • Ensure frame-rate independence (use deltaTime)
    • Avoid blocking operations
    • Pre-load resources
    • Use object pooling

    Next Steps

    Now that you understand performance optimization, learn about: - Memory Management - Manage memory efficiently - Platforms and Drivers - Platform-specific optimizations - Extensibility - Extend the engine


    See also: - Manual - Basic Rendering - Manual - Physics and Collisions

    \ No newline at end of file diff --git a/site/manual/optimization/platforms_and_drivers/index.html b/site/manual/optimization/platforms_and_drivers/index.html index f2923c4..1d1bf25 100644 --- a/site/manual/optimization/platforms_and_drivers/index.html +++ b/site/manual/optimization/platforms_and_drivers/index.html @@ -1,4 +1,4 @@ - Platforms and Drivers - PixelRoot32 Documentation

    Platforms and Drivers

    PixelRoot32 supports multiple platforms through driver abstraction. This guide covers supported platforms, display drivers, audio backends, and build configuration.

    Supported Platforms

    ESP32

    Primary platform for PixelRoot32 games.

    Characteristics: - TFT_eSPI display driver - Internal DAC or I2S audio - GPIO button input - Limited RAM/Flash - Real hardware constraints

    Use for: - Final game deployment - Hardware testing - Production builds

    Native/Desktop (SDL2)

    Development platform for rapid iteration.

    Characteristics: - SDL2 display driver - SDL2 audio backend - Keyboard input - Unlimited resources (for testing) - Fast development cycle

    Use for: - Development and debugging - Testing without hardware - Rapid prototyping - CI/CD testing

    Display Drivers

    TFT_eSPI (ESP32)

    TFT_eSPI is the display driver for ESP32, supporting many TFT displays.

    Optimizaciones ESP32

    Para maximizar el rendimiento en ESP32, PixelRoot32 utiliza:

    • DMA (Direct Memory Access): Las transferencias al display se realizan en segundo plano, permitiendo que la CPU prepare el siguiente frame mientras se envía el actual.
    • Doble Buffer con IRAM: El motor utiliza un buffer de pantalla (Sprite de TFT_eSPI) optimizado para transferencias rápidas.
    • Alineación de 16 bits: Los datos de sprites 2bpp/4bpp están alineados a palabras de 16 bits para aprovechar la arquitectura Xtensa.
    • IRAM_ATTR: Las funciones críticas de renderizado están marcadas para residir en la RAM de instrucciones, evitando cuellos de botella por acceso a la Flash.

    Configuración DMA

    El DMA se activa automáticamente si el hardware lo soporta. Asegúrate de configurar la frecuencia SPI adecuada para tu display (usualmente 40MHz u 80MHz).

    [env:esp32dev]
    + Platforms and Drivers - PixelRoot32 Documentation      

    Platforms and Drivers

    PixelRoot32 supports multiple platforms through driver abstraction. This guide covers supported platforms, display drivers, audio backends, and build configuration.

    Supported Platforms

    ESP32

    Primary platform for PixelRoot32 games.

    Characteristics: - TFT_eSPI display driver - Internal DAC or I2S audio - GPIO button input - Limited RAM/Flash - Real hardware constraints

    Use for: - Final game deployment - Hardware testing - Production builds

    Native/Desktop (SDL2)

    Development platform for rapid iteration.

    Characteristics: - SDL2 display driver - SDL2 audio backend - Keyboard input - Unlimited resources (for testing) - Fast development cycle

    Use for: - Development and debugging - Testing without hardware - Rapid prototyping - CI/CD testing

    Display Drivers

    TFT_eSPI (ESP32)

    TFT_eSPI is the display driver for ESP32, supporting many TFT displays.

    Optimizaciones ESP32

    Para maximizar el rendimiento en ESP32, PixelRoot32 utiliza:

    • DMA (Direct Memory Access): Las transferencias al display se realizan en segundo plano, permitiendo que la CPU prepare el siguiente frame mientras se envía el actual.
    • Doble Buffer con IRAM: El motor utiliza un buffer de pantalla (Sprite de TFT_eSPI) optimizado para transferencias rápidas.
    • Alineación de 16 bits: Los datos de sprites 2bpp/4bpp están alineados a palabras de 16 bits para aprovechar la arquitectura Xtensa.
    • IRAM_ATTR: Las funciones críticas de renderizado están marcadas para residir en la RAM de instrucciones, evitando cuellos de botella por acceso a la Flash.

    Configuración DMA

    El DMA se activa automáticamente si el hardware lo soporta. Asegúrate de configurar la frecuencia SPI adecuada para tu display (usualmente 40MHz u 80MHz).

    [env:esp32dev]
     build_flags = 
         -D ST7789_DRIVER          # Display type
         -D TFT_WIDTH=240          # Display width
    @@ -74,7 +74,8 @@
         -D PIXELROOT32_ENABLE_2BPP_SPRITES    # Enable 2bpp sprite format
         -D PIXELROOT32_ENABLE_4BPP_SPRITES   # Enable 4bpp sprite format
         -D PIXELROOT32_ENABLE_SCENE_ARENA    # Enable Scene Arena (experimental)
    -

    Scene limits (MAX_LAYERS / MAX_ENTITIES)

    You can override the default scene limits from your project without modifying the engine. The default of 3 for MAX_LAYERS is due to ESP32 platform constraints (memory and draw-loop cost); on native/PC you can use a higher value.

    Option A: Compiler flags (recommended) — in platformio.ini, add to build_flags for your environment:

    build_flags =
    +    -D PIXELROOT32_ENABLE_FPS_DISPLAY    # On-screen FPS counter (green, top-right; value updated every 8 frames)
    +

    FPS overlay (PIXELROOT32_ENABLE_FPS_DISPLAY)

    When defined, the engine draws an on-screen FPS counter (green text, top-right) each frame. The value is recalculated every 8 frames to keep per-frame cost low. No code changes are required; the overlay is drawn automatically after the scene. See API Reference - Engine - Optional: FPS overlay for details.

    Scene limits (MAX_LAYERS / MAX_ENTITIES)

    You can override the default scene limits from your project without modifying the engine. The default of 3 for MAX_LAYERS is due to ESP32 platform constraints (memory and draw-loop cost); on native/PC you can use a higher value.

    Option A: Compiler flags (recommended) — in platformio.ini, add to build_flags for your environment:

    build_flags =
         -DMAX_LAYERS=5
         -DMAX_ENTITIES=64
     

    The compiler defines these before any .cpp is processed. Because Scene.h uses #ifndef MAX_LAYERS / #ifndef MAX_ENTITIES, your values are used (more render layers drawn in Scene::draw, and on Arduino the entity queue capacity when built with MAX_ENTITIES).

    See API Reference - Scene - Overriding scene limits for details.

    Platform Detection

    #ifdef PLATFORM_ESP32
    @@ -169,4 +170,4 @@
         engine.run();
         return 0;
     }
    -

    Platform-Specific Considerations

    ESP32

    Memory: - Limited RAM (~320KB) - Use object pooling - Store data in flash - Avoid dynamic allocation

    Performance: - Target 30-60 FPS - Optimize rendering - Reduce entity count - Profile on hardware

    Hardware: - GPIO pin configuration - SPI display setup - Audio hardware connections - Power considerations

    Native

    Development: - Fast iteration - Easy debugging - Unlimited resources - Visual debugging tools

    Testing: - Test logic without hardware - Rapid prototyping - CI/CD integration - Cross-platform testing

    Troubleshooting

    ESP32 Display Issues

    • Check wiring connections
    • Verify pin numbers
    • Lower SPI frequency
    • Check display type matches
    • Verify power supply

    ESP32 Audio Issues

    • Check DAC/I2S pin configuration
    • Verify sample rate
    • Check hardware connections
    • Lower volume if distorted
    • Test with different sample rates

    Native Build Issues

    • Verify SDL2 installation
    • Check include/library paths
    • Ensure SDL2 version compatibility
    • Check linker flags

    Next Steps

    Now that you understand platforms and drivers, learn about: - Extensibility - Create custom drivers - Memory Management - ESP32 memory constraints - Performance Optimization - Platform-specific optimization


    See also: - API Reference - DrawSurface - API Reference - AudioBackend - Getting Started - Your First Project

    \ No newline at end of file +

    Platform-Specific Considerations

    ESP32

    Memory: - Limited RAM (~320KB) - Use object pooling - Store data in flash - Avoid dynamic allocation

    Performance: - Target 30-60 FPS - Optimize rendering - Reduce entity count - Profile on hardware

    Hardware: - GPIO pin configuration - SPI display setup - Audio hardware connections - Power considerations

    Native

    Development: - Fast iteration - Easy debugging - Unlimited resources - Visual debugging tools

    Testing: - Test logic without hardware - Rapid prototyping - CI/CD integration - Cross-platform testing

    Troubleshooting

    ESP32 Display Issues

    • Check wiring connections
    • Verify pin numbers
    • Lower SPI frequency
    • Check display type matches
    • Verify power supply

    ESP32 Audio Issues

    • Check DAC/I2S pin configuration
    • Verify sample rate
    • Check hardware connections
    • Lower volume if distorted
    • Test with different sample rates

    Native Build Issues

    • Verify SDL2 installation
    • Check include/library paths
    • Ensure SDL2 version compatibility
    • Check linker flags

    Next Steps

    Now that you understand platforms and drivers, learn about: - Extensibility - Create custom drivers - Memory Management - ESP32 memory constraints - Performance Optimization - Platform-specific optimization


    See also: - API Reference - DrawSurface - API Reference - AudioBackend - Getting Started - Your First Project

    \ No newline at end of file diff --git a/site/reference/api_overview/index.html b/site/reference/api_overview/index.html index 033fa57..36a65dd 100644 --- a/site/reference/api_overview/index.html +++ b/site/reference/api_overview/index.html @@ -4,4 +4,4 @@ }; }

    Constructors

    List of all constructors with parameters.

    Public Methods

    Method Description Parameters Returns
    methodName() Description param: type return type

    Properties

    Property Type Description
    property type Description

    Usage Example

    // Example code showing typical usage
    -

    Performance Notes

    Any performance considerations or limitations.

    See Also

    Links to related APIs and documentation.

    Finding APIs

    By Functionality

    By Module

    Navigate to the specific module folder: - api_reference/core/ - Core engine classes - api_reference/graphics/ - Rendering and graphics - api_reference/audio/ - Audio system - api_reference/physics/ - Physics and collisions - api_reference/ui/ - User interface

    Complete API List

    Core

    Graphics

    Audio

    Physics

    UI


    Note: This is an overview. For detailed API documentation, see the individual reference pages linked above.

    \ No newline at end of file +

    Performance Notes

    Any performance considerations or limitations.

    See Also

    Links to related APIs and documentation.

    Finding APIs

    By Functionality

    By Module

    Navigate to the specific module folder: - api_reference/core/ - Core engine classes - api_reference/graphics/ - Rendering and graphics - api_reference/audio/ - Audio system - api_reference/physics/ - Physics and collisions - api_reference/ui/ - User interface

    Complete API List

    Core

    Graphics

    Audio

    Physics

    UI


    Note: This is an overview. For detailed API documentation, see the individual reference pages linked above.

    \ No newline at end of file diff --git a/site/reference/code_examples/index.html b/site/reference/code_examples/index.html index 53d062e..98017ba 100644 --- a/site/reference/code_examples/index.html +++ b/site/reference/code_examples/index.html @@ -605,4 +605,4 @@ bool isActive() const { return active; } float getProgress() const { return static_cast<float>(elapsed) / duration; } }; -

    See Also

    \ No newline at end of file +

    See Also

    \ No newline at end of file diff --git a/site/reference/game_examples_guide/index.html b/site/reference/game_examples_guide/index.html index b29ca4e..e04a47c 100644 --- a/site/reference/game_examples_guide/index.html +++ b/site/reference/game_examples_guide/index.html @@ -1,4 +1,4 @@ - Game Examples Guide - PixelRoot32 Documentation

    Game Examples Guide

    This guide analyzes the complete game examples included with PixelRoot32, explaining their architecture, patterns, and lessons learned.

    Available Examples

    PixelRoot32 (en el proyecto PixelRoot32 Game Samples) incluye estos juegos y demos:

    • Metroidvania: Plataformas 2D con tilemap 4bpp multicapa y colisión tile-based (requiere PIXELROOT32_ENABLE_4BPP_SPRITES; sin scroll/cámara)
    • Space Invaders: Shooter completo con enemigos, proyectiles, búnkeres y audio
    • Pong: Clásico con física y colisiones
    • BrickBreaker: Estilo Breakout con partículas y audio avanzado
    • Snake: Juego en rejilla con entity pooling
    • TicTacToe: Turnos y IA simple
    • CameraDemo: Plataformas con cámara y parallax
    • SpritesDemo: Sprites 2bpp y 4bpp
    • TileMapDemo: Tilemaps 4bpp (con viewport culling)

    Space Invaders

    Location: src/examples/SpaceInvaders/

    Architecture

    Space Invaders demonstrates a complete game with multiple systems:

    • Scene Management: SpaceInvadersScene manages game state
    • Actor Hierarchy: PlayerActor, AlienActor, ProjectileActor, BunkerActor
    • Collision System: Uses collision layers for player, enemies, projectiles
    • Audio Integration: Sound effects for shooting, explosions, music
    • Background: Starfield (patrón de estrellas en código) o tilemap

    Key Systems

    Collision Layers

    namespace Layers {
    + Game Examples Guide - PixelRoot32 Documentation      

    Game Examples Guide

    This guide analyzes the complete game examples included with PixelRoot32, explaining their architecture, patterns, and lessons learned.

    Available Examples

    PixelRoot32 (in the PixelRoot32 Game Samples project) includes these games and demos:

    • Metroidvania: 2D platformer with multi-layer 4bpp tilemap and tile-based collision (requires PIXELROOT32_ENABLE_4BPP_SPRITES; no scroll/camera)
    • Space Invaders: Full shooter with enemies, projectiles, bunkers and audio
    • Pong: Classic with physics and collisions
    • BrickBreaker: Breakout-style with particles and advanced audio
    • Snake: Grid-based game with entity pooling
    • TicTacToe: Turn-based with simple AI
    • CameraDemo: Platformer with camera and parallax
    • SpritesDemo: 2bpp and 4bpp sprites
    • TileMapDemo: 4bpp tilemaps (with viewport culling)

    Space Invaders

    Location: src/examples/SpaceInvaders/

    Architecture

    Space Invaders demonstrates a complete game with multiple systems:

    • Scene Management: SpaceInvadersScene manages game state
    • Actor Hierarchy: PlayerActor, AlienActor, ProjectileActor, BunkerActor
    • Collision System: Uses collision layers for player, enemies, projectiles
    • Audio Integration: Sound effects for shooting, explosions, music
    • Background: Starfield (code-generated star pattern) or tilemap

    Key Systems

    Collision Layers

    namespace Layers {
         constexpr uint16_t PLAYER = 0x0001;
         constexpr uint16_t ALIEN = 0x0002;
         constexpr uint16_t PROJECTILE = 0x0004;
    @@ -12,7 +12,7 @@
     // Projectiles can hit aliens and bunkers
     projectile->setCollisionLayer(Layers::PROJECTILE);
     projectile->setCollisionMask(Layers::ALIEN | Layers::BUNKER);
    -

    Entity Management

    • Uses object pooling for projectiles
    • Manages alien formation with grid layout
    • Handles game state (playing, game over)

    Audio Integration

    • Background music using MusicPlayer
    • Sound effects for various events
    • Audio events triggered on collisions

    Patterns Used

    • Object Pooling: Projectiles are pooled and reused
    • State Machine: Game states (playing, game over, victory)
    • Grid Layout: Alien formation uses grid-based positioning
    • Event-Driven Audio: Sounds triggered by game events

    Lessons Learned

    • Collision layers are essential for complex games
    • Object pooling improves performance
    • Starfield or tilemap backgrounds are efficient
    • Audio enhances game feel significantly

    Metroidvania

    Ubicación: src/examples/Games/Metroidvania/

    Arquitectura

    Metroidvania es un ejemplo de plataformas 2D con tilemap multicapa y optimizaciones pensadas para ESP32. No usa scroll ni cámara; el nivel se dibuja con origen fijo (0,0).

    • Scene: MetroidvaniaScene con un único PlayerActor y varias capas de tilemap (background, platforms, details, stairs).
    • PlayerActor: Movimiento horizontal y vertical, escaleras, colisión tile-based (sin listas de rectángulos).
    • Tilemap: 4bpp (TileMap4bpp), con viewport culling y caché de paleta en el motor. Nivel 40×30 tiles (320×240 px).
    • Sin cámara: La vista no sigue al jugador; para scroll habría que usar Camera2D y aplicar offset en el renderer (como en CameraDemo).

    Características del motor usadas

    • Colisión tile-based: Comprobación directa de tiles alrededor del jugador (getTileAt), en lugar de iterar sobre platformRects.
    • Sprites 4bpp: Player con animaciones (idle, run, jump) desde headers generados (p. ej. Sprite Compiler).
    • Optimizaciones de renderizado: Viewport culling en drawTileMap, drawSprite 4bpp optimizado, capas culleadas por viewport.
    • Opcional: Scene arena, DMA, IRAM_ATTR en rutas críticas (según plan de optimización del ejemplo).

    Patrones utilizados

    • Tile-based collision: Un solo acceso por tile en O(1) en lugar de O(N) rectángulos.
    • Detección de escaleras: Un solo resultado reutilizado para colisión y cambio de estado.
    • Hitbox simplificada: Menos puntos de comprobación vertical (cabeza y pies).

    Lecciones

    • La colisión tile-based escala mejor que listas de rectángulos en niveles grandes.
    • Las optimizaciones de viewport y 4bpp mejoran FPS en ESP32.
    • Metroidvania sirve de referencia para juegos de plataformas con tilemap y cámara.

    Pong

    Location: src/examples/Pong/

    Architecture

    Pong demonstrates physics and collision handling:

    • PhysicsActor: Ball uses PhysicsActor for automatic physics
    • Collision Callbacks: Paddles and ball handle collisions
    • Score System: Simple score tracking and display
    • Game State: Reset and game over handling

    Key Systems

    Physics Setup

    class BallActor : public pixelroot32::core::PhysicsActor {
    +

    Entity Management

    • Uses object pooling for projectiles
    • Manages alien formation with grid layout
    • Handles game state (playing, game over)

    Audio Integration

    • Background music using MusicPlayer
    • Sound effects for various events
    • Audio events triggered on collisions

    Patterns Used

    • Object Pooling: Projectiles are pooled and reused
    • State Machine: Game states (playing, game over, victory)
    • Grid Layout: Alien formation uses grid-based positioning
    • Event-Driven Audio: Sounds triggered by game events

    Lessons Learned

    • Collision layers are essential for complex games
    • Object pooling improves performance
    • Starfield or tilemap backgrounds are efficient
    • Audio enhances game feel significantly

    Metroidvania

    Location: src/examples/Games/Metroidvania/

    Assets: Sprites and tilesets for this example come from the Tiny Metroidvania 8x8 pack by Kenmi (kenmi-art.itch.io).

    Architecture

    Metroidvania is a 2D platformer example with multi-layer tilemap and optimizations aimed at ESP32. It does not use scroll or camera; the level is drawn with a fixed origin (0,0).

    • Scene: MetroidvaniaScene with a single PlayerActor and several tilemap layers (background, platforms, details, stairs).
    • PlayerActor: Horizontal and vertical movement, stairs, tile-based collision (no rectangle lists).
    • Tilemap: 4bpp (TileMap4bpp), with viewport culling and palette cache in the engine. Level 40×30 tiles (320×240 px).
    • No camera: The view does not follow the player; for scroll you would use Camera2D and apply offset in the renderer (as in CameraDemo).

    Engine features used

    • Tile-based collision: Direct tile checks around the player (getTileAt), instead of iterating over platformRects.
    • 4bpp sprites: Player with animations (idle, run, jump) from generated headers (e.g. Sprite Compiler).
    • Rendering optimizations: Viewport culling in drawTileMap, optimized 4bpp drawSprite, layers culled by viewport.
    • Optional: Scene arena, DMA, IRAM_ATTR on critical paths (per example optimization plan).

    Patterns used

    • Tile-based collision: Single O(1) access per tile instead of O(N) rectangles.
    • Stair detection: Single result reused for collision and state change.
    • Simplified hitbox: Fewer vertical check points (head and feet).

    Lessons learned

    • Tile-based collision scales better than rectangle lists on large levels.
    • Viewport and 4bpp optimizations improve FPS on ESP32.
    • Metroidvania serves as a reference for platformers with tilemap and camera.

    Pong

    Location: src/examples/Pong/

    Architecture

    Pong demonstrates physics and collision handling:

    • PhysicsActor: Ball uses PhysicsActor for automatic physics
    • Collision Callbacks: Paddles and ball handle collisions
    • Score System: Simple score tracking and display
    • Game State: Reset and game over handling

    Key Systems

    Physics Setup

    class BallActor : public pixelroot32::core::PhysicsActor {
     public:
         BallActor(float x, float y, float speed, int radius)
             : PhysicsActor(x, y, radius * 2, radius * 2) {
    @@ -26,7 +26,7 @@
         // Modify velocity based on impact point
         // Play bounce sound
     }
    -

    Patterns Used

    • Physics Integration: Uses PhysicsActor for automatic movement
    • Collision Response: Custom collision handling
    • Score Management: Simple state tracking
    • Audio Feedback: Sound on collision

    Lessons Learned

    • PhysicsActor simplifies physics-based games
    • Collision callbacks allow custom response logic
    • Simple games can be very effective

    Snake

    Location: src/examples/Snake/

    Architecture

    Snake demonstrates entity pooling and grid-based movement:

    • Entity Pooling: Snake segments are pooled
    • Grid Movement: Movement constrained to grid
    • Game Logic: Food spawning, collision detection
    • State Management: Game over, reset functionality

    Key Systems

    Entity Pooling

    class SnakeScene {
    +

    Patterns Used

    • Physics Integration: Uses PhysicsActor for automatic movement
    • Collision Response: Custom collision handling
    • Score Management: Simple state tracking
    • Audio Feedback: Sound on collision

    Lessons Learned

    • PhysicsActor simplifies physics-based games
    • Collision callbacks allow custom response logic
    • Simple games can be very effective

    Snake

    Location: src/examples/Snake/

    Architecture

    Snake demonstrates entity pooling and grid-based movement:

    • Entity Pooling: Snake segments are pooled
    • Grid Movement: Movement constrained to grid
    • Game Logic: Food spawning, collision detection
    • State Management: Game over, reset functionality

    Key Systems

    Entity Pooling

    class SnakeScene {
     private:
         std::vector<SnakeSegmentActor*> segmentPool;
         std::vector<SnakeSegmentActor*> snakeSegments;
    @@ -54,7 +54,7 @@
             this->y = cellY * CELL_SIZE;
         }
     };
    -

    Patterns Used

    • Object Pooling: Segments are pre-allocated and reused
    • Grid System: Discrete grid-based movement
    • Linked List: Snake segments form a linked structure
    • Food Spawning: Random food placement with collision checking

    Lessons Learned

    • Entity pooling is essential for dynamic entities
    • Grid-based movement simplifies collision detection
    • Pre-allocation avoids memory fragmentation

    TicTacToe

    Location: src/examples/TicTacToe/

    Architecture

    TicTacToe demonstrates turn-based logic and simple AI:

    • Turn Management: Player vs AI turns
    • Game Board: 3x3 grid representation
    • Win Detection: Check for winning conditions
    • Simple AI: Random move selection

    Key Systems

    Board Representation

    class TicTacToeScene {
    +

    Patterns Used

    • Object Pooling: Segments are pre-allocated and reused
    • Grid System: Discrete grid-based movement
    • Linked List: Snake segments form a linked structure
    • Food Spawning: Random food placement with collision checking

    Lessons Learned

    • Entity pooling is essential for dynamic entities
    • Grid-based movement simplifies collision detection
    • Pre-allocation avoids memory fragmentation

    TicTacToe

    Location: src/examples/TicTacToe/

    Architecture

    TicTacToe demonstrates turn-based logic and simple AI:

    • Turn Management: Player vs AI turns
    • Game Board: 3x3 grid representation
    • Win Detection: Check for winning conditions
    • Simple AI: Random move selection

    Key Systems

    Board Representation

    class TicTacToeScene {
     private:
         int board[3][3];  // 0=empty, 1=X, 2=O
         bool playerTurn = true;
    @@ -77,7 +77,7 @@
         // Check columns, diagonals...
         return 0; // No winner
     }
    -

    Patterns Used

    • State Machine: Turn-based state management
    • Grid Logic: 2D array for board representation
    • Simple AI: Random valid move selection
    • UI Integration: Buttons for player input

    Lessons Learned

    • Turn-based games are straightforward to implement
    • Simple AI can be effective for basic games
    • Grid-based logic is easy to reason about

    CameraDemo

    Location: src/examples/CameraDemo/

    Architecture

    CameraDemo demonstrates scrolling and parallax:

    • Camera2D: Camera following player
    • Tilemap: Level built with tilemap
    • Parallax: Multiple background layers
    • Platformer Physics: Player with jumping and gravity

    Key Systems

    Camera Setup

    class CameraDemoScene {
    +

    Patterns Used

    • State Machine: Turn-based state management
    • Grid Logic: 2D array for board representation
    • Simple AI: Random valid move selection
    • UI Integration: Buttons for player input

    Lessons Learned

    • Turn-based games are straightforward to implement
    • Simple AI can be effective for basic games
    • Grid-based logic is easy to reason about

    CameraDemo

    Location: src/examples/CameraDemo/

    Architecture

    CameraDemo demonstrates scrolling and parallax:

    • Camera2D: Camera following player
    • Tilemap: Level built with tilemap
    • Parallax: Multiple background layers
    • Platformer Physics: Player with jumping and gravity

    Key Systems

    Camera Setup

    class CameraDemoScene {
     private:
         pixelroot32::graphics::Camera2D camera;
         float levelWidth;
    @@ -108,7 +108,7 @@
             PhysicsActor::update(deltaTime);
         }
     };
    -

    Patterns Used

    • Camera Following: Dead-zone camera following
    • Tilemap Rendering: Efficient level rendering
    • Parallax Scrolling: Multiple background layers
    • Platform Collision: Custom collision with platforms

    Lessons Learned

    • Camera system enables large levels
    • Tilemaps are efficient for level data
    • Parallax adds depth to 2D games
    • Platform collision requires custom logic

    SpritesDemo

    Location: src/examples/SpritesDemo/

    Architecture

    SpritesDemo showcases advanced sprite formats:

    • 2bpp Sprites: 4-color sprite format
    • 4bpp Sprites: 16-color sprite format (if enabled)
    • Animation: Sprite animation examples
    • Format Comparison: Side-by-side format display

    Key Systems

    2bpp Sprite Usage

    #ifdef PIXELROOT32_ENABLE_2BPP_SPRITES
    +

    Patterns Used

    • Camera Following: Dead-zone camera following
    • Tilemap Rendering: Efficient level rendering
    • Parallax Scrolling: Multiple background layers
    • Platform Collision: Custom collision with platforms

    Lessons Learned

    • Camera system enables large levels
    • Tilemaps are efficient for level data
    • Parallax adds depth to 2D games
    • Platform collision requires custom logic

    SpritesDemo

    Location: src/examples/SpritesDemo/

    Architecture

    SpritesDemo showcases advanced sprite formats:

    • 2bpp Sprites: 4-color sprite format
    • 4bpp Sprites: 16-color sprite format (if enabled)
    • Animation: Sprite animation examples
    • Format Comparison: Side-by-side format display

    Key Systems

    2bpp Sprite Usage

    #ifdef PIXELROOT32_ENABLE_2BPP_SPRITES
     static const pixelroot32::graphics::Sprite2bpp SPRITE_2BPP = {
         SPRITE_DATA,
         SPRITE_PALETTE,
    @@ -131,7 +131,7 @@
             }
         }
     };
    -

    Patterns Used

    • Format Comparison: Shows different sprite formats
    • Animation Loop: Frame-based animation
    • Conditional Compilation: Uses build flags

    Lessons Learned

    • Advanced formats provide more color options
    • Animation is straightforward with frame arrays
    • Build flags enable/disable experimental features

    Common Patterns Across Examples

    Scene Initialization

    All examples follow this pattern:

    void init() override {
    +

    Patterns Used

    • Format Comparison: Shows different sprite formats
    • Animation Loop: Frame-based animation
    • Conditional Compilation: Uses build flags

    Lessons Learned

    • Advanced formats provide more color options
    • Animation is straightforward with frame arrays
    • Build flags enable/disable experimental features

    Common Patterns Across Examples

    Screen Resolution

    All examples are configured for a 240x240 screen resolution.

    Scene Initialization

    All examples follow this pattern:

    void init() override {
         // 1. Set palette
         pixelroot32::graphics::setPalette(PaletteType::NES);
     
    @@ -171,4 +171,4 @@
         // 4. Draw UI/HUD
         drawHUD(renderer);
     }
    -

    Learning Path

    Beginner Examples

    1. Pong: Física y colisiones básicas
    2. TicTacToe: Lógica por turnos
    3. Snake: Entity pooling y rejilla

    Intermediate Examples

    1. CameraDemo: Cámara y parallax
    2. SpritesDemo: Formatos 2bpp y 4bpp
    3. BrickBreaker: Física, partículas y audio

    Advanced Examples

    1. Space Invaders: Juego completo (sprites 1bpp, colisiones, audio)
    2. Metroidvania: Plataformas con tilemap 4bpp multicapa, colisión tile-based y optimizaciones ESP32 (sin scroll/cámara)

    Code Study Recommendations

    For Learning Physics

    • Study Pong/BallActor.cpp - PhysicsActor usage
    • Study CameraDemo/PlayerCube.cpp - Platformer physics

    For Learning Collisions

    • Study SpaceInvaders - Complex collision layers
    • Study Pong - Simple collision response

    For Learning Memory Management

    • Study Snake/SnakeScene.cpp - Entity pooling
    • Study SpaceInvaders - Projectile pooling

    For Learning Audio

    • Study SpaceInvaders - Music and sound effects
    • Study Pong - Simple audio integration

    For Learning UI

    • Study TicTacToe - Button-based UI
    • Study menu scenes - Layout usage

    Extending Examples

    Adding Features

    • Pong: Add power-ups, multiple balls
    • Snake: Add obstacles, multiple food types
    • Space Invaders: Add boss battles, power-ups

    Creating Variations

    • Pong: Make it vertical, add walls
    • Snake: Change to hexagonal grid
    • TicTacToe: Make it 4x4 or 5x5

    Best Practices from Examples

    1. Pre-allocate Resources: All examples pre-allocate entities
    2. Use Object Pooling: For frequently created/destroyed entities
    3. Organize by Layers: Clear collision layer organization
    4. Separate Concerns: Game logic separate from rendering
    5. State Management: Clear game state handling

    See Also


    Note: All example code is available in the src/examples/ directory of the PixelRoot32 Game Samples project.

    \ No newline at end of file +

    Learning Path

    Beginner Examples

    1. Pong: Basic physics and collisions
    2. TicTacToe: Turn-based logic
    3. Snake: Entity pooling and grid

    Intermediate Examples

    1. CameraDemo: Camera and parallax
    2. SpritesDemo: 2bpp and 4bpp formats
    3. BrickBreaker: Physics, particles and audio

    Advanced Examples

    1. Space Invaders: Full game (1bpp sprites, collisions, audio)
    2. Metroidvania: Platformer with multi-layer 4bpp tilemap, tile-based collision and ESP32 optimizations (no scroll/camera)

    Code Study Recommendations

    For Learning Physics

    • Study Pong/BallActor.cpp - PhysicsActor usage
    • Study CameraDemo/PlayerCube.cpp - Platformer physics

    For Learning Collisions

    • Study SpaceInvaders - Complex collision layers
    • Study Pong - Simple collision response

    For Learning Memory Management

    • Study Snake/SnakeScene.cpp - Entity pooling
    • Study SpaceInvaders - Projectile pooling

    For Learning Audio

    • Study SpaceInvaders - Music and sound effects
    • Study Pong - Simple audio integration

    For Learning UI

    • Study TicTacToe - Button-based UI
    • Study menu scenes - Layout usage

    Extending Examples

    Adding Features

    • Pong: Add power-ups, multiple balls
    • Snake: Add obstacles, multiple food types
    • Space Invaders: Add boss battles, power-ups

    Creating Variations

    • Pong: Make it vertical, add walls
    • Snake: Change to hexagonal grid
    • TicTacToe: Make it 4x4 or 5x5

    Best Practices from Examples

    1. Pre-allocate Resources: All examples pre-allocate entities
    2. Use Object Pooling: For frequently created/destroyed entities
    3. Organize by Layers: Clear collision layer organization
    4. Separate Concerns: Game logic separate from rendering
    5. State Management: Clear game state handling

    See Also


    Note: All example code is available in the src/examples/ directory of the PixelRoot32 Game Samples project.

    \ No newline at end of file diff --git a/site/resources/available_tools/index.html b/site/resources/available_tools/index.html index 2a22eef..480a146 100644 --- a/site/resources/available_tools/index.html +++ b/site/resources/available_tools/index.html @@ -61,4 +61,4 @@ #!/bin/bash pr32-sprite-compiler assets/sprites/*.png --output-dir src/sprites/ # Continue with build... -

    Best Practices

    • Organize assets: Keep source images separate from generated code
    • Version control: Commit generated headers, not source images (or both)
    • Naming conventions: Use consistent naming for sprites
    • Batch processing: Process multiple sprites at once when possible

    See Also


    Note: Tool availability may vary. Check the PixelRoot32 repository for the latest tool information.

    \ No newline at end of file +

    Best Practices

    • Organize assets: Keep source images separate from generated code
    • Version control: Commit generated headers, not source images (or both)
    • Naming conventions: Use consistent naming for sprites
    • Batch processing: Process multiple sprites at once when possible

    See Also


    Note: Tool availability may vary. Check the PixelRoot32 repository for the latest tool information.

    \ No newline at end of file diff --git a/site/resources/faq/index.html b/site/resources/faq/index.html index c35c2a6..23abd0f 100644 --- a/site/resources/faq/index.html +++ b/site/resources/faq/index.html @@ -11,4 +11,4 @@ Serial.print("Free heap: "); Serial.println(ESP.getFreeHeap()); #endif -

    Hardware

    Which ESP32 board should I use?

    Any ESP32 board works. Common choices: - ESP32-WROOM-32 - ESP32-WROVER (more RAM) - ESP32-DevKit

    Which display should I use?

    Popular choices: - ST7789: 240x240, good quality - ST7735: 128x128, smaller/cheaper - ILI9341: 240x320, larger

    See Platforms and Drivers.

    How many buttons do I need?

    Minimum: 4 (UP, DOWN, LEFT, RIGHT) Recommended: 6 (add A and B buttons) More buttons can be added if needed.

    Can I use analog joysticks?

    Not directly supported. You can read analog pins manually and convert to digital input, but the engine expects digital buttons.

    Troubleshooting

    My display is blank. What's wrong?

    1. Check wiring connections
    2. Verify pin numbers in platformio.ini
    3. Lower SPI frequency
    4. Check display type matches hardware
    5. Verify power supply

    See Troubleshooting.

    Buttons don't work. Why?

    1. Check button wiring
    2. Verify pin numbers in InputConfig
    3. Check pull-up/pull-down resistors
    4. Ensure InputManager is being updated
    5. Test with isButtonDown() vs isButtonPressed()

    Audio is distorted. How do I fix it?

    1. Lower volume levels
    2. Reduce sample rate (try 11025 Hz)
    3. Check for too many simultaneous sounds
    4. Verify hardware connections
    5. Check power supply

    Game crashes randomly. What's happening?

    Common causes: - Out of memory - Too many entities - Infinite loops - Stack overflow - Watchdog timeout

    See Troubleshooting for debugging techniques.

    Advanced

    Can I extend the engine?

    Yes, PixelRoot32 is designed to be extensible. You can: - Create custom display drivers - Create custom audio backends - Extend existing systems

    See Extensibility.

    Can I use 3D graphics?

    No, PixelRoot32 is 2D-only. It's designed for sprite-based 2D games.

    Can I add networking?

    Not currently supported. The engine focuses on single-player games.

    Can I save game data?

    Not currently supported. A save system is planned for future versions.

    Can I use multiple scenes at once?

    Yes, use SceneManager to push/pop scenes. This is useful for menus and pause screens.

    Getting Help

    Where can I get help?

    • Documentation: This documentation site
    • Examples: Study example games in the samples project
    • Discord: Community Discord server
    • GitHub: Open an issue for bugs

    How do I report a bug?

    Create a detailed bug report with: - Platform (ESP32 or Native) - Hardware details - Minimal reproduction code - Error messages - Expected vs actual behavior

    Can I contribute?

    Yes! PixelRoot32 is open source. Check the main repository for contribution guidelines.

    See Also


    Can't find your question? Check the Troubleshooting guide or ask on the Discord server.

    \ No newline at end of file +

    Hardware

    Which ESP32 board should I use?

    Any ESP32 board works. Common choices: - ESP32-WROOM-32 - ESP32-WROVER (more RAM) - ESP32-DevKit

    Which display should I use?

    Popular choices: - ST7789: 240x240, good quality - ST7735: 128x128, smaller/cheaper - ILI9341: 240x320, larger

    See Platforms and Drivers.

    How many buttons do I need?

    Minimum: 4 (UP, DOWN, LEFT, RIGHT) Recommended: 6 (add A and B buttons) More buttons can be added if needed.

    Can I use analog joysticks?

    Not directly supported. You can read analog pins manually and convert to digital input, but the engine expects digital buttons.

    Troubleshooting

    My display is blank. What's wrong?

    1. Check wiring connections
    2. Verify pin numbers in platformio.ini
    3. Lower SPI frequency
    4. Check display type matches hardware
    5. Verify power supply

    See Troubleshooting.

    Buttons don't work. Why?

    1. Check button wiring
    2. Verify pin numbers in InputConfig
    3. Check pull-up/pull-down resistors
    4. Ensure InputManager is being updated
    5. Test with isButtonDown() vs isButtonPressed()

    Audio is distorted. How do I fix it?

    1. Lower volume levels
    2. Reduce sample rate (try 11025 Hz)
    3. Check for too many simultaneous sounds
    4. Verify hardware connections
    5. Check power supply

    Game crashes randomly. What's happening?

    Common causes: - Out of memory - Too many entities - Infinite loops - Stack overflow - Watchdog timeout

    See Troubleshooting for debugging techniques.

    Advanced

    Can I extend the engine?

    Yes, PixelRoot32 is designed to be extensible. You can: - Create custom display drivers - Create custom audio backends - Extend existing systems

    See Extensibility.

    Can I use 3D graphics?

    No, PixelRoot32 is 2D-only. It's designed for sprite-based 2D games.

    Can I add networking?

    Not currently supported. The engine focuses on single-player games.

    Can I save game data?

    Not currently supported. A save system is planned for future versions.

    Can I use multiple scenes at once?

    Yes, use SceneManager to push/pop scenes. This is useful for menus and pause screens.

    Getting Help

    Where can I get help?

    • Documentation: This documentation site
    • Examples: Study example games in the samples project
    • Discord: Community Discord server
    • GitHub: Open an issue for bugs

    How do I report a bug?

    Create a detailed bug report with: - Platform (ESP32 or Native) - Hardware details - Minimal reproduction code - Error messages - Expected vs actual behavior

    Can I contribute?

    Yes! PixelRoot32 is open source. Check the main repository for contribution guidelines.

    See Also


    Can't find your question? Check the Troubleshooting guide or ask on the Discord server.

    \ No newline at end of file diff --git a/site/resources/limitations_and_considerations/index.html b/site/resources/limitations_and_considerations/index.html index 8a56e97..b86840d 100644 --- a/site/resources/limitations_and_considerations/index.html +++ b/site/resources/limitations_and_considerations/index.html @@ -1 +1 @@ - Limitations and Considerations - PixelRoot32 Documentation

    Limitations and Considerations

    This document honestly documents what PixelRoot32 can and cannot do, helping you make informed decisions about using the engine.

    Hardware Limitations (ESP32)

    Memory Constraints

    RAM: - Available: ~320KB total (varies by ESP32 model) - Heap: Limited and fragmented over time - Stack: ~8KB, avoid large stack allocations - Impact: Limits entity count, sprite data, and dynamic allocation

    Flash: - Available: 4MB+ (varies by model) - Usage: Program code, sprite data, assets - Impact: Large games may approach limits

    Recommendations: - Use object pooling - Store data in flash (const/constexpr) - Avoid dynamic allocation in game loop - Keep entity count low

    CPU Limitations

    Performance: - Clock Speed: 240MHz (typically) - Single-threaded: One core handles everything - Target FPS: 30-60 FPS (depends on complexity) - Frame Budget: ~16-33ms per frame at 60 FPS

    Impact: - Complex games may struggle - Many entities reduce performance - Expensive calculations hurt FPS

    Recommendations: - Optimize rendering - Reduce entity count - Cache calculations - Profile on hardware

    Display Limitations

    Supported Displays: - TFT displays via SPI (ST7735, ST7789, ILI9341, etc.) - Limited to SPI displays - Resolution typically 128x128 to 320x240

    Constraints: - SPI communication speed limits - Display initialization complexity - Power consumption

    Audio Limitations

    Hardware: - Internal DAC: Lower quality, simple setup - I2S: Higher quality, requires external DAC - Sample rates: 11025 Hz (DAC) or 22050 Hz (I2S)

    Constraints: - 4 channels total (2 Pulse, 1 Triangle, 1 Noise) - Music uses one channel - Limited simultaneous sounds - Quality limited by hardware

    Software Limitations

    Entity System

    MAX_ENTITIES = 32 per scene - Hard limit, cannot be changed easily - Applies to all entities (actors, UI, particles, etc.) - Must manage entity count carefully

    Workarounds: - Use object pooling - Reuse entities - Disable entities instead of removing - Combine multiple entities into one

    No RTTI (Runtime Type Information)

    Impact: - Cannot use dynamic_cast in most code - Type checking must be done manually - Limits polymorphism patterns

    Alternatives: - Use virtual functions - Manual type checking - Tag-based systems

    No Exceptions in Critical Code

    Impact: - Cannot use try/catch in game loop - Error handling must be explicit - Crashes instead of exceptions

    Best Practices: - Validate inputs - Check return values - Use assertions for debugging - Handle errors explicitly

    No Dynamic Allocation in Game Loop

    Impact: - Cannot use new/delete during gameplay - Must pre-allocate resources - Limits flexibility

    Solutions: - Object pooling - Pre-allocation in init() - Static buffers - Fixed-size arrays

    No Advanced Features

    Not Supported: - 3D graphics - Shaders - Advanced physics (joints, constraints) - Networking - File system (ESP32) - Advanced audio effects

    Focus: - 2D sprite-based games - Simple physics - Retro-style games - Embedded-friendly features

    Experimental Features

    2bpp Sprites

    Status: Experimental - Requires PIXELROOT32_ENABLE_2BPP_SPRITES flag - May have bugs or limitations - Not fully tested

    Use with caution: - Test thoroughly - May change in future versions - Report issues if found

    4bpp Sprites

    Status: Experimental - Requires PIXELROOT32_ENABLE_4BPP_SPRITES flag - More experimental than 2bpp - Higher memory usage

    Use with caution: - Test extensively - Monitor memory usage - May be unstable

    Scene Arena

    Status: Experimental - Requires PIXELROOT32_ENABLE_SCENE_ARENA flag - Alternative memory management - May have bugs

    Recommendations: - Use object pooling instead (more stable) - Test thoroughly if using - May be removed or changed

    Unsupported Features (Current)

    Planned but Not Available

    • u8g2 Driver: Alternative display driver (planned)
    • Music Compiler: Tool to convert music files (planned)
    • Tilemap Compiler: Tool to create tilemaps (planned)
    • Save System: Persistent storage system (planned)
    • Spatial Partitioning: Advanced collision optimization (planned)

    Not Planned

    • 3D Graphics: 2D-only engine
    • Networking: No network support
    • File System: No file I/O on ESP32
    • Advanced Audio: NES-like audio only
    • Scripting: No Lua/JavaScript support

    Best Practices for ESP32

    Memory Management

    • Pre-allocate: All resources in init()
    • Object pooling: Reuse entities
    • Flash storage: Use const/constexpr for data
    • Avoid strings: Use static buffers
    • Monitor usage: Check heap regularly

    Performance

    • Limit entities: Stay well below MAX_ENTITIES
    • Optimize rendering: Use culling, batching
    • Cache calculations: Avoid repeated work
    • Profile on hardware: PC performance ≠ ESP32

    Development

    • Test on hardware: Don't rely only on Native
    • Start simple: Add complexity gradually
    • Monitor memory: Watch for leaks
    • Optimize incrementally: Profile and optimize

    What PixelRoot32 IS Good For

    Retro-style 2D gamesArcade gamesPuzzle gamesPlatformersShootersEducational projectsPrototypingEmbedded game development

    What PixelRoot32 is NOT Good For

    3D gamesComplex physics simulationsLarge open worldsGames requiring many entitiesGames with complex graphicsNetwork multiplayerGames requiring file I/O

    Making Informed Decisions

    Before Starting a Project

    1. Assess requirements: Does PixelRoot32 fit?
    2. Check limitations: Can you work within constraints?
    3. Plan architecture: Design around limitations
    4. Test early: Verify on hardware early

    If Limitations Are a Problem

    Consider alternatives: - Full game engines (Unity, Godot) for complex games - Custom solutions for specific needs - Different hardware for more resources

    Or work within limits: - Simplify game design - Optimize aggressively - Use creative solutions

    Version Compatibility

    Current Version

    • Engine Version: 0.2.0-dev
    • API Stability: May change
    • Breaking Changes: Possible in future versions

    Recommendations: - Pin exact version in platformio.ini - Don't use ^ or fuzzy versioning - Test after engine updates - Review changelog

    Honest Assessment

    PixelRoot32 is designed for: - Simple to medium complexity games - Retro/arcade style - ESP32 hardware constraints - Rapid development

    It is not designed for: - AAA game complexity - Modern graphics - Large-scale games - Unlimited resources

    See Also


    Remember: Understanding limitations helps you build better games within PixelRoot32's capabilities.

    \ No newline at end of file + Limitations and Considerations - PixelRoot32 Documentation

    Limitations and Considerations

    This document honestly documents what PixelRoot32 can and cannot do, helping you make informed decisions about using the engine.

    Hardware Limitations (ESP32)

    Memory Constraints

    RAM: - Available: ~320KB total (varies by ESP32 model) - Heap: Limited and fragmented over time - Stack: ~8KB, avoid large stack allocations - Impact: Limits entity count, sprite data, and dynamic allocation

    Flash: - Available: 4MB+ (varies by model) - Usage: Program code, sprite data, assets - Impact: Large games may approach limits

    Recommendations: - Use object pooling - Store data in flash (const/constexpr) - Avoid dynamic allocation in game loop - Keep entity count low

    CPU Limitations

    Performance: - Clock Speed: 240MHz (typically) - Single-threaded: One core handles everything - Target FPS: 30-60 FPS (depends on complexity) - Frame Budget: ~16-33ms per frame at 60 FPS

    Impact: - Complex games may struggle - Many entities reduce performance - Expensive calculations hurt FPS

    Recommendations: - Optimize rendering - Reduce entity count - Cache calculations - Profile on hardware

    Display Limitations

    Supported Displays: - TFT displays via SPI (ST7735, ST7789, ILI9341, etc.) - Limited to SPI displays - Resolution typically 128x128 to 320x240

    Constraints: - SPI communication speed limits - Display initialization complexity - Power consumption

    Audio Limitations

    Hardware: - Internal DAC: Lower quality, simple setup - I2S: Higher quality, requires external DAC - Sample rates: 11025 Hz (DAC) or 22050 Hz (I2S)

    Constraints: - 4 channels total (2 Pulse, 1 Triangle, 1 Noise) - Music uses one channel - Limited simultaneous sounds - Quality limited by hardware

    Software Limitations

    Entity System

    MAX_ENTITIES = 32 per scene - Hard limit, cannot be changed easily - Applies to all entities (actors, UI, particles, etc.) - Must manage entity count carefully

    Workarounds: - Use object pooling - Reuse entities - Disable entities instead of removing - Combine multiple entities into one

    No RTTI (Runtime Type Information)

    Impact: - Cannot use dynamic_cast in most code - Type checking must be done manually - Limits polymorphism patterns

    Alternatives: - Use virtual functions - Manual type checking - Tag-based systems

    No Exceptions in Critical Code

    Impact: - Cannot use try/catch in game loop - Error handling must be explicit - Crashes instead of exceptions

    Best Practices: - Validate inputs - Check return values - Use assertions for debugging - Handle errors explicitly

    No Dynamic Allocation in Game Loop

    Impact: - Cannot use new/delete during gameplay - Must pre-allocate resources - Limits flexibility

    Solutions: - Object pooling - Pre-allocation in init() - Static buffers - Fixed-size arrays

    No Advanced Features

    Not Supported: - 3D graphics - Shaders - Advanced physics (joints, constraints) - Networking - File system (ESP32) - Advanced audio effects

    Focus: - 2D sprite-based games - Simple physics - Retro-style games - Embedded-friendly features

    Experimental Features

    2bpp Sprites

    Status: Experimental - Requires PIXELROOT32_ENABLE_2BPP_SPRITES flag - May have bugs or limitations - Not fully tested

    Use with caution: - Test thoroughly - May change in future versions - Report issues if found

    4bpp Sprites

    Status: Experimental - Requires PIXELROOT32_ENABLE_4BPP_SPRITES flag - More experimental than 2bpp - Higher memory usage

    Use with caution: - Test extensively - Monitor memory usage - May be unstable

    Scene Arena

    Status: Experimental - Requires PIXELROOT32_ENABLE_SCENE_ARENA flag - Alternative memory management - May have bugs

    Recommendations: - Use object pooling instead (more stable) - Test thoroughly if using - May be removed or changed

    Unsupported Features (Current)

    Planned but Not Available

    • u8g2 Driver: Alternative display driver (planned)
    • Music Compiler: Tool to convert music files (planned)
    • Tilemap Compiler: Tool to create tilemaps (planned)
    • Save System: Persistent storage system (planned)
    • Spatial Partitioning: Advanced collision optimization (planned)

    Not Planned

    • 3D Graphics: 2D-only engine
    • Networking: No network support
    • File System: No file I/O on ESP32
    • Advanced Audio: NES-like audio only
    • Scripting: No Lua/JavaScript support

    Best Practices for ESP32

    Memory Management

    • Pre-allocate: All resources in init()
    • Object pooling: Reuse entities
    • Flash storage: Use const/constexpr for data
    • Avoid strings: Use static buffers
    • Monitor usage: Check heap regularly

    Performance

    • Limit entities: Stay well below MAX_ENTITIES
    • Optimize rendering: Use culling, batching
    • Cache calculations: Avoid repeated work
    • Profile on hardware: PC performance ≠ ESP32

    Development

    • Test on hardware: Don't rely only on Native
    • Start simple: Add complexity gradually
    • Monitor memory: Watch for leaks
    • Optimize incrementally: Profile and optimize

    What PixelRoot32 IS Good For

    Retro-style 2D gamesArcade gamesPuzzle gamesPlatformersShootersEducational projectsPrototypingEmbedded game development

    What PixelRoot32 is NOT Good For

    3D gamesComplex physics simulationsLarge open worldsGames requiring many entitiesGames with complex graphicsNetwork multiplayerGames requiring file I/O

    Making Informed Decisions

    Before Starting a Project

    1. Assess requirements: Does PixelRoot32 fit?
    2. Check limitations: Can you work within constraints?
    3. Plan architecture: Design around limitations
    4. Test early: Verify on hardware early

    If Limitations Are a Problem

    Consider alternatives: - Full game engines (Unity, Godot) for complex games - Custom solutions for specific needs - Different hardware for more resources

    Or work within limits: - Simplify game design - Optimize aggressively - Use creative solutions

    Version Compatibility

    Current Version

    • Engine Version: 0.2.0-dev
    • API Stability: May change
    • Breaking Changes: Possible in future versions

    Recommendations: - Pin exact version in platformio.ini - Don't use ^ or fuzzy versioning - Test after engine updates - Review changelog

    Honest Assessment

    PixelRoot32 is designed for: - Simple to medium complexity games - Retro/arcade style - ESP32 hardware constraints - Rapid development

    It is not designed for: - AAA game complexity - Modern graphics - Large-scale games - Unlimited resources

    See Also


    Remember: Understanding limitations helps you build better games within PixelRoot32's capabilities.

    \ No newline at end of file diff --git a/site/resources/troubleshooting/index.html b/site/resources/troubleshooting/index.html index ef0a1d7..d58b1d5 100644 --- a/site/resources/troubleshooting/index.html +++ b/site/resources/troubleshooting/index.html @@ -71,4 +71,4 @@ // Usage DEBUG_LOG("Entity created"); DEBUG_LOG("Collision detected"); -

    Getting Help

    If you can't resolve an issue:

    1. Check documentation: Review relevant guides
    2. Search examples: Look at example games
    3. Review code: Check engine source code
    4. Community: Ask on Discord or GitHub
    5. Report issue: Create detailed bug report

    Bug Report Template

    When reporting issues, include:

    • Platform: ESP32 or Native
    • Hardware: Display type, ESP32 model
    • Code: Minimal reproduction code
    • Error messages: Full error output
    • Expected behavior: What should happen
    • Actual behavior: What actually happens
    • Steps to reproduce: How to trigger the issue

    See Also


    Note: Many issues are configuration-related. Double-check your setup before assuming a bug.

    \ No newline at end of file +

    Getting Help

    If you can't resolve an issue:

    1. Check documentation: Review relevant guides
    2. Search examples: Look at example games
    3. Review code: Check engine source code
    4. Community: Ask on Discord or GitHub
    5. Report issue: Create detailed bug report

    Bug Report Template

    When reporting issues, include:

    • Platform: ESP32 or Native
    • Hardware: Display type, ESP32 model
    • Code: Minimal reproduction code
    • Error messages: Full error output
    • Expected behavior: What should happen
    • Actual behavior: What actually happens
    • Steps to reproduce: How to trigger the issue

    See Also


    Note: Many issues are configuration-related. Double-check your setup before assuming a bug.

    \ No newline at end of file diff --git a/site/search/search_index.json b/site/search/search_index.json index f6b1279..eb1a2d1 100644 --- a/site/search/search_index.json +++ b/site/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"PixelRoot32 Documentation","text":"

    PixelRoot32 is a lightweight 2D game engine designed for ESP32 and native desktop targets. This site provides official, versioned documentation with clear guides, conceptual explanations, API references, and complete examples to help you build games efficiently.

    "},{"location":"#quick-links","title":"Quick Links","text":"
    • What is PixelRoot32? - Start here to understand the engine
    • Your First Project - Get up and running quickly
    • Fundamental Concepts - Learn the core concepts
    • Manual - Complete user guide
    • API Reference - Complete API documentation
    • Examples - Complete game examples
    • Tools - Available tools
    • FAQ - FAQ and troubleshooting
    "},{"location":"#getting-started","title":"Getting Started","text":"

    New to PixelRoot32? Follow this learning path:

    1. What is PixelRoot32? - Understand what the engine is and what it can do
    2. Why PixelRoot32? - Learn the advantages and use cases
    3. Fundamental Concepts - Learn the core architecture concepts
    4. Your First Project - Create and run your first project
    "},{"location":"#about-this-documentation","title":"About This Documentation","text":"
    • Professional technical English across all pages
    • Search-enabled, mobile-friendly UI
    • Versioned with mike (stable/dev/experimental)
    • Cross-linked concepts, API, and examples
    • Progressive learning path from basics to advanced topics
    "},{"location":"api_reference/audio/audio_config/","title":"AudioConfig","text":"

    Configuration for the Audio subsystem.

    "},{"location":"api_reference/audio/audio_config/#description","title":"Description","text":"

    AudioConfig is a simple struct that holds configuration settings for the audio system, including the audio backend and sample rate. It is passed to AudioEngine during construction.

    "},{"location":"api_reference/audio/audio_config/#namespace","title":"Namespace","text":"
    namespace pixelroot32::audio {\n    struct AudioConfig {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/audio/audio_config/#structure","title":"Structure","text":""},{"location":"api_reference/audio/audio_config/#audiobackend-backend","title":"AudioBackend* backend","text":"

    Pointer to the platform-specific audio backend implementation.

    Type: AudioBackend*

    Access: Read-write

    Default: nullptr

    Notes: - Must be set to a valid backend instance - Backend is platform-specific: - ESP32: ESP32_DAC_AudioBackend or ESP32_I2S_AudioBackend - Native: SDL2_AudioBackend - Backend manages the actual audio hardware/API

    Example:

    #ifdef PLATFORM_ESP32\n    pixelroot32::drivers::esp32::ESP32_DAC_AudioBackend dacBackend;\n    audioConfig.backend = &dacBackend;\n#elif PLATFORM_NATIVE\n    pixelroot32::drivers::native::SDL2_AudioBackend sdlBackend;\n    audioConfig.backend = &sdlBackend;\n#endif\n

    "},{"location":"api_reference/audio/audio_config/#int-samplerate","title":"int sampleRate","text":"

    Desired sample rate in Hz.

    Type: int

    Access: Read-write

    Default: 22050

    Notes: - Common values: 11025, 22050, 44100 - Lower rates use less CPU and memory (better for ESP32) - Higher rates provide better quality - Must match backend capabilities

    Example:

    audioConfig.sampleRate = 11025;  // Lower quality, less CPU (ESP32)\naudioConfig.sampleRate = 22050;  // Balanced (default)\naudioConfig.sampleRate = 44100; // Higher quality (Native)\n

    "},{"location":"api_reference/audio/audio_config/#constructors","title":"Constructors","text":""},{"location":"api_reference/audio/audio_config/#audioconfigaudiobackend-backend-nullptr-int-samplerate-22050","title":"AudioConfig(AudioBackend* backend = nullptr, int sampleRate = 22050)","text":"

    Default constructor.

    Parameters: - backend (AudioBackend*, optional): Pointer to the audio backend implementation. Default: nullptr - sampleRate (int, optional): Desired sample rate in Hz. Default: 22050

    Example:

    // Default construction\npixelroot32::audio::AudioConfig audioConfig;\n\n// With backend\npixelroot32::drivers::esp32::ESP32_DAC_AudioBackend dacBackend;\npixelroot32::audio::AudioConfig audioConfig(&dacBackend, 11025);\n

    "},{"location":"api_reference/audio/audio_config/#usage-example","title":"Usage Example","text":""},{"location":"api_reference/audio/audio_config/#esp32-with-dac-backend","title":"ESP32 with DAC Backend","text":"
    #ifdef PLATFORM_ESP32\n#include \"drivers/esp32/ESP32_DAC_AudioBackend.h\"\n\npixelroot32::drivers::esp32::ESP32_DAC_AudioBackend dacBackend;\n\npixelroot32::audio::AudioConfig audioConfig;\naudioConfig.backend = &dacBackend;\naudioConfig.sampleRate = 11025;  // Lower rate for ESP32\n\npixelroot32::audio::AudioEngine audioEngine(audioConfig);\naudioEngine.init();\n#endif\n
    "},{"location":"api_reference/audio/audio_config/#esp32-with-i2s-backend","title":"ESP32 with I2S Backend","text":"
    #ifdef PLATFORM_ESP32\n#include \"drivers/esp32/ESP32_I2S_AudioBackend.h\"\n\npixelroot32::drivers::esp32::ESP32_I2S_AudioBackend i2sBackend;\n\npixelroot32::audio::AudioConfig audioConfig;\naudioConfig.backend = &i2sBackend;\naudioConfig.sampleRate = 22050;  // Higher quality with I2S\n\npixelroot32::audio::AudioEngine audioEngine(audioConfig);\naudioEngine.init();\n#endif\n
    "},{"location":"api_reference/audio/audio_config/#native-with-sdl2-backend","title":"Native with SDL2 Backend","text":"
    #ifdef PLATFORM_NATIVE\n#include \"drivers/native/SDL2_AudioBackend.h\"\n\npixelroot32::drivers::native::SDL2_AudioBackend sdlBackend;\n\npixelroot32::audio::AudioConfig audioConfig;\naudioConfig.backend = &sdlBackend;\naudioConfig.sampleRate = 44100;  // High quality for PC\n\npixelroot32::audio::AudioEngine audioEngine(audioConfig);\naudioEngine.init();\n#endif\n
    "},{"location":"api_reference/audio/audio_config/#complete-engine-setup","title":"Complete Engine Setup","text":"
    #include \"core/Engine.h\"\n#include \"graphics/DisplayConfig.h\"\n#include \"input/InputConfig.h\"\n#include \"audio/AudioConfig.h\"\n\nvoid setup() {\n    // Display config\n    pixelroot32::graphics::DisplayConfig displayConfig;\n    displayConfig.width = 128;\n    displayConfig.height = 128;\n\n    // Input config\n    pixelroot32::input::InputConfig inputConfig;\n    // ... configure input\n\n    // Audio config\n    #ifdef PLATFORM_ESP32\n        pixelroot32::drivers::esp32::ESP32_DAC_AudioBackend dacBackend;\n        pixelroot32::audio::AudioConfig audioConfig(&dacBackend, 11025);\n    #elif PLATFORM_NATIVE\n        pixelroot32::drivers::native::SDL2_AudioBackend sdlBackend;\n        pixelroot32::audio::AudioConfig audioConfig(&sdlBackend, 44100);\n    #endif\n\n    // Create engine with all configs\n    pixelroot32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n    engine.init();\n    engine.run();\n}\n
    "},{"location":"api_reference/audio/audio_config/#platform-specific-considerations","title":"Platform-Specific Considerations","text":""},{"location":"api_reference/audio/audio_config/#esp32-dac-backend","title":"ESP32 DAC Backend","text":"
    • Sample rate: 11025 Hz recommended (lower CPU usage)
    • Quality: Lower quality, but simple setup
    • Pin: Uses GPIO 25 or 26
    • Hardware: Requires simple amplifier circuit
    "},{"location":"api_reference/audio/audio_config/#esp32-i2s-backend","title":"ESP32 I2S Backend","text":"
    • Sample rate: 22050 Hz recommended
    • Quality: Higher quality than DAC
    • Pins: Requires I2S pins (BCLK, LRCK, DOUT)
    • Hardware: Requires external I2S DAC
    "},{"location":"api_reference/audio/audio_config/#native-sdl2-backend","title":"Native SDL2 Backend","text":"
    • Sample rate: 44100 Hz typical
    • Quality: High quality
    • Setup: Requires SDL2 library installed
    • Platforms: Windows, Linux, macOS
    "},{"location":"api_reference/audio/audio_config/#performance-considerations","title":"Performance Considerations","text":"
    • Sample rate: Lower rates use less CPU and memory
    • Backend choice: DAC is simpler but lower quality than I2S
    • Buffer size: Configured in backend, affects latency vs stability
    "},{"location":"api_reference/audio/audio_config/#see-also","title":"See Also","text":"
    • AudioEngine - Audio playback engine
    • Manual - Audio
    • Manual - Platforms and Drivers
    • API Overview
    "},{"location":"api_reference/audio/audio_engine/","title":"AudioEngine","text":"

    Core class for the NES-like audio subsystem.

    "},{"location":"api_reference/audio/audio_engine/#description","title":"Description","text":"

    AudioEngine manages the audio channels (Pulse, Triangle, Noise), mixes their output, and provides the audio stream to the backend. It implements a NES-like audio system with 4 fixed channels: 2 Pulse channels, 1 Triangle channel, and 1 Noise channel.

    The engine is event-driven: you trigger sound effects via playEvent(), and the engine automatically manages channel allocation and playback.

    "},{"location":"api_reference/audio/audio_engine/#namespace","title":"Namespace","text":"
    namespace pixelroot32::audio {\n    class AudioEngine {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/audio/audio_engine/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Engine (manages audio engine instance)
    "},{"location":"api_reference/audio/audio_engine/#constructors","title":"Constructors","text":""},{"location":"api_reference/audio/audio_engine/#audioengineconst-audioconfig-config","title":"AudioEngine(const AudioConfig& config)","text":"

    Constructs the AudioEngine with the given configuration.

    Parameters: - config (const AudioConfig&): Configuration struct containing the backend and parameters (sample rate, etc.)

    Example:

    #include \"audio/AudioEngine.h\"\n#include \"audio/AudioConfig.h\"\n\npixelroot32::audio::AudioConfig audioConfig;\naudioConfig.backend = &audioBackend;  // Platform-specific backend\naudioConfig.sampleRate = 22050;       // 22.05 kHz for retro feel\n\npixelroot32::audio::AudioEngine audioEngine(audioConfig);\naudioEngine.init();\n

    "},{"location":"api_reference/audio/audio_engine/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/audio/audio_engine/#void-init","title":"void init()","text":"

    Initializes the audio subsystem and the backend.

    Returns: - void

    Notes: - Must be called after construction and before use - Initializes the platform-specific audio backend - Safe to call multiple times (idempotent) - Typically called automatically by Engine::init()

    Example:

    AudioEngine audioEngine(audioConfig);\naudioEngine.init();  // Initialize before use\n

    "},{"location":"api_reference/audio/audio_engine/#void-updateunsigned-long-deltatime","title":"void update(unsigned long deltaTime)","text":"

    Updates the audio state based on game time.

    Parameters: - deltaTime (unsigned long): Time elapsed since last frame in milliseconds

    Returns: - void

    Notes: - Should be called from the main game loop (typically via Engine::update()) - Updates channel lifetimes and durations - Automatically stops channels when their duration expires - Must be called every frame for proper audio timing

    Example:

    void update(unsigned long deltaTime) override {\n    // Update audio (called automatically by Engine)\n    engine.getAudioEngine().update(deltaTime);\n\n    // Your game logic...\n}\n

    "},{"location":"api_reference/audio/audio_engine/#void-generatesamplesint16_t-stream-int-length","title":"void generateSamples(int16_t* stream, int length)","text":"

    Fills the provided buffer with mixed audio samples.

    Parameters: - stream (int16_t*): Pointer to the buffer to fill - length (int): Number of samples to generate

    Returns: - void

    Notes: - This method is typically called by the AudioBackend from an audio callback or task - Not usually called directly by game code - Generates 16-bit signed integer PCM samples - Mixes all active channels into a mono stream

    Advanced Usage:

    // Typically not called directly, but if implementing custom backend:\nint16_t buffer[512];\naudioEngine.generateSamples(buffer, 512);\n

    "},{"location":"api_reference/audio/audio_engine/#void-playeventconst-audioevent-event","title":"void playEvent(const AudioEvent& event)","text":"

    Triggers a one-shot sound effect.

    Parameters: - event (const AudioEvent&): The audio event to play

    Returns: - void

    Notes: - Automatically finds an available channel of the correct type - If no channel is available, the event may be dropped (no error) - Events are fire-and-forget (no need to track playback) - Use for sound effects, not background music

    Example:

    // Play a jump sound\npixelroot32::audio::AudioEvent jumpSound{};\njumpSound.type = pixelroot32::audio::WaveType::PULSE;\njumpSound.frequency = 800.0f;\njumpSound.duration = 0.1f;\njumpSound.volume = 0.7f;\njumpSound.duty = 0.5f;\n\nauto& audio = engine.getAudioEngine();\naudio.playEvent(jumpSound);\n\n// Play an explosion sound\npixelroot32::audio::AudioEvent explosion{};\nexplosion.type = pixelroot32::audio::WaveType::NOISE;\nexplosion.frequency = 1000.0f;\nexplosion.duration = 0.3f;\nexplosion.volume = 0.9f;\n\naudio.playEvent(explosion);\n

    "},{"location":"api_reference/audio/audio_engine/#void-setmastervolumefloat-volume","title":"void setMasterVolume(float volume)","text":"

    Sets the master volume for all audio output.

    Parameters: - volume (float): Volume level (0.0 = silent, 1.0 = full volume)

    Returns: - void

    Notes: - Affects all channels and events - Clamped to [0.0, 1.0] range - Use for volume control menus or mute functionality

    Example:

    auto& audio = engine.getAudioEngine();\naudio.setMasterVolume(0.5f);  // 50% volume\naudio.setMasterVolume(0.0f);  // Mute\naudio.setMasterVolume(1.0f);  // Full volume\n

    "},{"location":"api_reference/audio/audio_engine/#float-getmastervolume-const","title":"float getMasterVolume() const","text":"

    Gets the current master volume.

    Returns: - float: Current master volume (0.0 to 1.0)

    Example:

    float currentVolume = audioEngine.getMasterVolume();\n

    "},{"location":"api_reference/audio/audio_engine/#audio-channels","title":"Audio Channels","text":"

    The engine manages 4 fixed channels:

    1. Channel 0: Pulse wave
    2. Channel 1: Pulse wave
    3. Channel 2: Triangle wave
    4. Channel 3: Noise wave

    Notes: - Channels are automatically allocated when playing events - If all channels of a type are busy, new events may be dropped - Background music typically uses one channel (via MusicPlayer)

    "},{"location":"api_reference/audio/audio_engine/#usage-example","title":"Usage Example","text":"
    #include \"audio/AudioEngine.h\"\n#include \"audio/AudioConfig.h\"\n\nclass MyScene : public pixelroot32::core::Scene {\nprivate:\n    void playJumpSound() {\n        auto& audio = engine.getAudioEngine();\n\n        pixelroot32::audio::AudioEvent sound{};\n        sound.type = pixelroot32::audio::WaveType::PULSE;\n        sound.frequency = 800.0f;\n        sound.duration = 0.1f;\n        sound.volume = 0.7f;\n        sound.duty = 0.5f;\n\n        audio.playEvent(sound);\n    }\n\n    void playHitSound() {\n        auto& audio = engine.getAudioEngine();\n\n        pixelroot32::audio::AudioEvent sound{};\n        sound.type = pixelroot32::audio::WaveType::NOISE;\n        sound.frequency = 500.0f;\n        sound.duration = 0.05f;\n        sound.volume = 0.5f;\n\n        audio.playEvent(sound);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Audio is updated automatically by Engine\n        // Just play events when needed\n        if (playerJumped) {\n            playJumpSound();\n            playerJumped = false;\n        }\n    }\n};\n
    "},{"location":"api_reference/audio/audio_engine/#performance-considerations","title":"Performance Considerations","text":"
    • Channel limit: Only 4 channels total; plan sound effects accordingly
    • Event dropping: If all channels are busy, new events are silently dropped
    • Update frequency: update() must be called every frame for proper timing
    • Sample generation: generateSamples() is called by backend at audio rate (not game rate)
    "},{"location":"api_reference/audio/audio_engine/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Sample rate: Lower sample rates (11025 Hz) use less CPU and memory
    • Backend choice: DAC backend is simpler but lower quality than I2S
    • Buffer size: Larger buffers reduce underruns but increase latency
    • Channel management: Limit simultaneous sounds to avoid channel conflicts
    "},{"location":"api_reference/audio/audio_engine/#see-also","title":"See Also","text":"
    • AudioConfig - Audio configuration
    • AudioTypes - Audio data structures
    • MusicPlayer - Background music playback
    • Manual - Audio
    • API Overview
    "},{"location":"api_reference/audio/audio_types/","title":"Audio Types","text":"

    Data structures and types for the audio system.

    "},{"location":"api_reference/audio/audio_types/#description","title":"Description","text":"

    This document describes the data structures used by the audio system, including wave types, audio events, and channel state.

    "},{"location":"api_reference/audio/audio_types/#namespace","title":"Namespace","text":"
    namespace pixelroot32::audio {\n    // Types and structures\n}\n
    "},{"location":"api_reference/audio/audio_types/#wavetype-enum","title":"WaveType Enum","text":"

    Defines the types of waveforms available.

    Values: - WaveType::PULSE: Pulse wave (square wave with variable duty cycle) - WaveType::TRIANGLE: Triangle wave (smooth, melodic) - WaveType::NOISE: Noise wave (random, percussive)

    Example:

    pixelroot32::audio::WaveType wave = pixelroot32::audio::WaveType::PULSE;\n

    "},{"location":"api_reference/audio/audio_types/#audioevent-structure","title":"AudioEvent Structure","text":"

    A fire-and-forget sound event triggered by the game.

    Members: - WaveType type: Type of waveform to use - float frequency: Frequency in Hz - float duration: Duration in seconds - float volume: Volume level (0.0 to 1.0) - float duty: Duty cycle for pulse wave (0.0 to 1.0, pulse only)

    Example:

    pixelroot32::audio::AudioEvent jumpSound{};\njumpSound.type = pixelroot32::audio::WaveType::PULSE;\njumpSound.frequency = 800.0f;\njumpSound.duration = 0.1f;\njumpSound.volume = 0.7f;\njumpSound.duty = 0.5f;\n\nauto& audio = engine.getAudioEngine();\naudio.playEvent(jumpSound);\n

    "},{"location":"api_reference/audio/audio_types/#common-sound-effects","title":"Common Sound Effects","text":"

    Jump Sound:

    pixelroot32::audio::AudioEvent jump{};\njump.type = pixelroot32::audio::WaveType::PULSE;\njump.frequency = 800.0f;\njump.duration = 0.1f;\njump.volume = 0.7f;\njump.duty = 0.5f;\n

    Hit Sound:

    pixelroot32::audio::AudioEvent hit{};\nhit.type = pixelroot32::audio::WaveType::NOISE;\nhit.frequency = 500.0f;\nhit.duration = 0.05f;\nhit.volume = 0.5f;\n

    Collect Sound:

    pixelroot32::audio::AudioEvent collect{};\ncollect.type = pixelroot32::audio::WaveType::TRIANGLE;\ncollect.frequency = 1000.0f;\ncollect.duration = 0.15f;\ncollect.volume = 0.6f;\n

    Explosion:

    pixelroot32::audio::AudioEvent explosion{};\nexplosion.type = pixelroot32::audio::WaveType::NOISE;\nexplosion.frequency = 200.0f;\nexplosion.duration = 0.3f;\nexplosion.volume = 0.9f;\n

    "},{"location":"api_reference/audio/audio_types/#audiochannel-structure","title":"AudioChannel Structure","text":"

    Represents the internal state of a single audio channel.

    Members: - bool enabled: Whether the channel is active - WaveType type: Type of waveform - float frequency: Current frequency in Hz - float phase: Current phase (0.0 to 1.0) - float phaseIncrement: Pre-calculated phase increment - float volume: Current volume (0.0 to 1.0) - float targetVolume: Target volume for envelopes - float dutyCycle: Duty cycle for pulse wave (0.0 to 1.0) - uint16_t noiseRegister: LFSR state for noise generation - unsigned long durationMs: Total duration in milliseconds - unsigned long remainingMs: Remaining duration in milliseconds

    Methods: - void reset(): Resets the channel to inactive state

    Notes: - Internal structure, typically not accessed directly - Managed automatically by AudioEngine - 4 channels total: 2 Pulse, 1 Triangle, 1 Noise

    "},{"location":"api_reference/audio/audio_types/#frequency-reference","title":"Frequency Reference","text":"

    Common frequencies for musical notes (A4 = 440 Hz):

    • C4: 261.63 Hz
    • D4: 293.66 Hz
    • E4: 329.63 Hz
    • F4: 349.23 Hz
    • G4: 392.00 Hz
    • A4: 440.00 Hz
    • B4: 493.88 Hz
    • C5: 523.25 Hz

    Example:

    // Play a C note\npixelroot32::audio::AudioEvent note{};\nnote.type = pixelroot32::audio::WaveType::PULSE;\nnote.frequency = 261.63f;  // C4\nnote.duration = 0.5f;\nnote.volume = 0.8f;\nnote.duty = 0.5f;\n

    "},{"location":"api_reference/audio/audio_types/#duty-cycle-pulse-wave","title":"Duty Cycle (Pulse Wave)","text":"

    Duty cycle controls the shape of the pulse wave:

    • 0.125 (12.5%): Thin pulse (NES-like)
    • 0.25 (25%): Narrow pulse
    • 0.5 (50%): Square wave (most common)
    • 0.75 (75%): Wide pulse

    Example:

    // Thin pulse (NES style)\nevent.duty = 0.125f;\n\n// Square wave (standard)\nevent.duty = 0.5f;\n

    "},{"location":"api_reference/audio/audio_types/#usage-examples","title":"Usage Examples","text":""},{"location":"api_reference/audio/audio_types/#creating-sound-effect-library","title":"Creating Sound Effect Library","text":"
    namespace SoundEffects {\n    // Jump sound\n    inline pixelroot32::audio::AudioEvent jump() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::PULSE;\n        evt.frequency = 800.0f;\n        evt.duration = 0.1f;\n        evt.volume = 0.7f;\n        evt.duty = 0.5f;\n        return evt;\n    }\n\n    // Hit sound\n    inline pixelroot32::audio::AudioEvent hit() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::NOISE;\n        evt.frequency = 500.0f;\n        evt.duration = 0.05f;\n        evt.volume = 0.5f;\n        return evt;\n    }\n\n    // Collect sound\n    inline pixelroot32::audio::AudioEvent collect() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::TRIANGLE;\n        evt.frequency = 1000.0f;\n        evt.duration = 0.15f;\n        evt.volume = 0.6f;\n        return evt;\n    }\n}\n\n// Usage\nauto& audio = engine.getAudioEngine();\naudio.playEvent(SoundEffects::jump());\naudio.playEvent(SoundEffects::hit());\n
    "},{"location":"api_reference/audio/audio_types/#frequency-sweep-effect","title":"Frequency Sweep Effect","text":"
    void playSweepSound() {\n    auto& audio = engine.getAudioEngine();\n\n    // Create multiple events for sweep effect\n    for (int i = 0; i < 5; i++) {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::PULSE;\n        evt.frequency = 400.0f + (i * 200.0f);  // Sweep from 400 to 1200 Hz\n        evt.duration = 0.05f;\n        evt.volume = 0.6f;\n        evt.duty = 0.5f;\n\n        audio.playEvent(evt);\n        delay(50);  // Small delay between events\n    }\n}\n
    "},{"location":"api_reference/audio/audio_types/#performance-considerations","title":"Performance Considerations","text":"
    • Event creation: Creating events is fast (just struct initialization)
    • Channel allocation: Events are queued and played when channels are available
    • Frequency range: Keep frequencies in reasonable range (100-5000 Hz) for best results
    • Duration: Shorter durations free channels faster
    "},{"location":"api_reference/audio/audio_types/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Events are small structs, safe to create frequently
    • CPU: Audio generation is efficient but limit simultaneous sounds
    • Quality: Lower sample rates reduce CPU usage
    "},{"location":"api_reference/audio/audio_types/#see-also","title":"See Also","text":"
    • AudioEngine - Audio playback engine
    • AudioConfig - Audio configuration
    • Manual - Audio
    • API Overview
    "},{"location":"api_reference/audio/music_player/","title":"MusicPlayer","text":"

    Lightweight sequencer built on top of AudioEngine to play background melodies as tracks.

    "},{"location":"api_reference/audio/music_player/#description","title":"Description","text":"

    MusicPlayer is a simple sequencer that plays MusicTrack structures. It advances notes based on game time, converts MusicNote entries to AudioEvent calls, and manages playback state (play, stop, pause, resume).

    The player uses one audio channel (typically a Pulse channel) for music, leaving other channels available for sound effects.

    "},{"location":"api_reference/audio/music_player/#namespace","title":"Namespace","text":"
    namespace pixelroot32::audio {\n    class MusicPlayer {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/audio/music_player/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Engine (manages music player instance)
    "},{"location":"api_reference/audio/music_player/#constructors","title":"Constructors","text":""},{"location":"api_reference/audio/music_player/#musicplayeraudioengine-engine","title":"MusicPlayer(AudioEngine& engine)","text":"

    Constructs the MusicPlayer.

    Parameters: - engine (AudioEngine&): Reference to the AudioEngine used to play sounds

    Notes: - Typically created and managed by Engine - Access via engine.getMusicPlayer()

    Example:

    auto& audio = engine.getAudioEngine();\npixelroot32::audio::MusicPlayer musicPlayer(audio);\n

    "},{"location":"api_reference/audio/music_player/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/audio/music_player/#void-playconst-musictrack-track","title":"void play(const MusicTrack& track)","text":"

    Starts playing a track.

    Parameters: - track (const MusicTrack&): The track to play

    Returns: - void

    Notes: - Stops any currently playing track - Starts from the beginning of the track - If track has loop = true, will loop automatically - Uses one audio channel (typically Pulse)

    Example:

    static const MusicNote MELODY[] = {\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::E, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::G, 0.25f),\n    makeRest(0.10f),\n};\n\nstatic const MusicTrack GAME_MUSIC = {\n    MELODY,\n    sizeof(MELODY) / sizeof(MusicNote),\n    true,  // loop\n    WaveType::PULSE,\n    0.5f   // volume\n};\n\nvoid init() override {\n    auto& music = engine.getMusicPlayer();\n    music.play(GAME_MUSIC);\n}\n

    "},{"location":"api_reference/audio/music_player/#void-stop","title":"void stop()","text":"

    Stops playback and silences the channel.

    Returns: - void

    Notes: - Immediately stops the current note - Resets playback to the beginning - Channel is freed for other use

    Example:

    void onGameOver() {\n    auto& music = engine.getMusicPlayer();\n    music.stop();\n}\n

    "},{"location":"api_reference/audio/music_player/#void-pause","title":"void pause()","text":"

    Pauses playback.

    Returns: - void

    Notes: - Current note continues until it ends, then playback pauses - Playback state is preserved (can resume from where it paused) - Use for pause menus

    Example:

    void onPause() {\n    auto& music = engine.getMusicPlayer();\n    music.pause();\n}\n

    "},{"location":"api_reference/audio/music_player/#void-resume","title":"void resume()","text":"

    Resumes playback.

    Returns: - void

    Notes: - Only works if playback was paused - Resumes from where it was paused - Use to unpause after pause menu

    Example:

    void onResume() {\n    auto& music = engine.getMusicPlayer();\n    music.resume();\n}\n

    "},{"location":"api_reference/audio/music_player/#void-updateunsigned-long-deltatime","title":"void update(unsigned long deltaTime)","text":"

    Updates the player state. Should be called every frame.

    Parameters: - deltaTime (unsigned long): Time elapsed since last frame in milliseconds

    Returns: - void

    Notes: - Must be called every frame for proper timing - Advances note playback based on elapsed time - Automatically plays next notes in sequence - Typically called automatically by Engine

    Example:

    // Called automatically by Engine, but can be called manually:\nvoid update(unsigned long deltaTime) override {\n    Scene::update(deltaTime);\n\n    // Music player is updated automatically by Engine\n    // No need to call manually\n}\n

    "},{"location":"api_reference/audio/music_player/#bool-isplaying-const","title":"bool isPlaying() const","text":"

    Checks if a track is currently playing.

    Returns: - bool: true if playing, false otherwise

    Notes: - Returns false if stopped or paused - Use to check playback state before operations

    Example:

    auto& music = engine.getMusicPlayer();\nif (music.isPlaying()) {\n    // Music is active\n} else {\n    // Music is stopped or paused\n}\n

    "},{"location":"api_reference/audio/music_player/#void-settempofactorfloat-factor","title":"void setTempoFactor(float factor)","text":"

    Sets the global tempo scaling factor.

    Parameters: - factor (float): Tempo multiplier - 1.0f: Normal speed - 2.0f: Double speed - 0.5f: Half speed

    Returns: - void

    Notes: - Affects all note durations - Useful for speed-up effects or slow-motion - Applied to all tracks

    Example:

    auto& music = engine.getMusicPlayer();\nmusic.setTempoFactor(1.5f);  // 50% faster\nmusic.setTempoFactor(0.5f);   // 50% slower\nmusic.setTempoFactor(1.0f);   // Normal speed\n

    "},{"location":"api_reference/audio/music_player/#float-gettempofactor-const","title":"float getTempoFactor() const","text":"

    Gets the current tempo scaling factor.

    Returns: - float: Current factor (default 1.0f)

    Example:

    float currentTempo = musicPlayer.getTempoFactor();\n

    "},{"location":"api_reference/audio/music_player/#musictrack-structure","title":"MusicTrack Structure","text":"

    A MusicTrack contains:

    • notes (const MusicNote*): Array of music notes
    • noteCount (size_t): Number of notes in the array
    • loop (bool): Whether to loop the track
    • waveType (WaveType): Wave type to use (typically PULSE)
    • volume (float): Volume level (0.0 to 1.0)
    "},{"location":"api_reference/audio/music_player/#musicnote-structure","title":"MusicNote Structure","text":"

    A MusicNote contains:

    • instrument (InstrumentPreset): Instrument preset to use
    • note (Note): Musical note (C, D, E, etc.)
    • duration (float): Duration in seconds

    Use helper functions: - makeNote(instrument, note, duration): Create a note - makeRest(duration): Create a rest (silence)

    "},{"location":"api_reference/audio/music_player/#usage-example","title":"Usage Example","text":"
    #include \"audio/MusicPlayer.h\"\n#include \"audio/AudioMusicTypes.h\"\n\nusing namespace pixelroot32::audio;\n\n// Define a simple melody\nstatic const MusicNote MAIN_THEME[] = {\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.25f),\n    makeNote(INSTR_PULSE_LEAD, Note::E, 0.25f),\n    makeNote(INSTR_PULSE_LEAD, Note::G, 0.25f),\n    makeRest(0.1f),\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.5f),\n    makeRest(0.2f),\n};\n\nstatic const MusicTrack MAIN_THEME_TRACK = {\n    MAIN_THEME,\n    sizeof(MAIN_THEME) / sizeof(MusicNote),\n    true,           // loop\n    WaveType::PULSE,\n    0.6f            // volume\n};\n\nclass GameScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Start background music\n        auto& music = engine.getMusicPlayer();\n        music.play(MAIN_THEME_TRACK);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Music updates automatically\n    }\n\n    void onPauseMenu() {\n        auto& music = engine.getMusicPlayer();\n        music.pause();\n    }\n\n    void onResumeGame() {\n        auto& music = engine.getMusicPlayer();\n        music.resume();\n    }\n\n    void onGameOver() {\n        auto& music = engine.getMusicPlayer();\n        music.stop();\n    }\n};\n
    "},{"location":"api_reference/audio/music_player/#performance-considerations","title":"Performance Considerations","text":"
    • One channel: Music uses one channel, leaving others for sound effects
    • Update frequency: update() must be called every frame
    • Track size: Larger tracks use more memory (store in flash)
    • Tempo factor: Changing tempo is fast (just a multiplier)
    "},{"location":"api_reference/audio/music_player/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Store tracks in flash (const/constexpr) to save RAM
    • CPU: Music playback is lightweight (simple sequencing)
    • Channel conflict: Music and sound effects share channels; plan accordingly
    "},{"location":"api_reference/audio/music_player/#see-also","title":"See Also","text":"
    • AudioEngine - Audio playback engine
    • AudioTypes - Audio data structures
    • AudioMusicTypes - Music data structures
    • Manual - Audio
    • API Overview
    "},{"location":"api_reference/core/actor/","title":"Actor","text":"

    An Entity capable of physical interaction and collision.

    "},{"location":"api_reference/core/actor/#description","title":"Description","text":"

    Actor extends Entity with collision layers and masks. Actors are used for dynamic game objects like players, enemies, projectiles, and obstacles that need to interact with each other through collision detection.

    Actors participate in the collision system and can detect collisions with other actors based on their collision layers and masks.

    "},{"location":"api_reference/core/actor/#namespace","title":"Namespace","text":"
    namespace pixelroot32::core {\n    class Actor : public Entity {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/actor/#inheritance","title":"Inheritance","text":"
    • Inherits from: Entity
    • Inherited by: PhysicsActor and your custom actor classes
    "},{"location":"api_reference/core/actor/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/actor/#actorfloat-x-float-y-int-w-int-h","title":"Actor(float x, float y, int w, int h)","text":"

    Creates a new actor with specified position and size.

    Parameters: - x (float): Initial X position in world space - y (float): Initial Y position in world space - w (int): Actor width in pixels - h (int): Actor height in pixels

    Notes: - Actor type is automatically set to EntityType::ACTOR - Collision layer and mask default to DefaultLayers::kNone - Must set collision layer and mask for collision detection to work

    Example:

    class PlayerActor : public pixelroot32::core::Actor {\npublic:\n    PlayerActor(float x, float y) \n        : Actor(x, y, 16, 16) {\n        // Set collision layer and mask\n        layer = pixelroot32::physics::DefaultLayers::kPlayer;\n        mask = pixelroot32::physics::DefaultLayers::kEnemy | \n               pixelroot32::physics::DefaultLayers::kObstacle;\n    }\n\n    void update(unsigned long deltaTime) override {\n        Actor::update(deltaTime);\n        // Player logic\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawSprite(playerSprite, \n                           static_cast<int>(x), \n                           static_cast<int>(y), \n                           Color::White);\n    }\n\n    Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(Actor* other) override {\n        // Handle collision\n    }\n};\n

    "},{"location":"api_reference/core/actor/#public-properties","title":"Public Properties","text":""},{"location":"api_reference/core/actor/#collisionlayer-layer","title":"CollisionLayer layer","text":"

    The collision layer this actor belongs to.

    Type: pixelroot32::physics::CollisionLayer (uint16_t)

    Access: Read-write

    Default: DefaultLayers::kNone

    Notes: - Defines which layer this actor is on - Use bit flags to assign multiple layers (e.g., kPlayer | kProjectile) - Only actors with matching layers in their mask will collide

    Example:

    actor->layer = pixelroot32::physics::DefaultLayers::kPlayer;\n

    "},{"location":"api_reference/core/actor/#collisionlayer-mask","title":"CollisionLayer mask","text":"

    The collision layers this actor interacts with.

    Type: pixelroot32::physics::CollisionLayer (uint16_t)

    Access: Read-write

    Default: DefaultLayers::kNone

    Notes: - Defines which layers this actor can collide with - Use bit flags to check multiple layers (e.g., kEnemy | kObstacle) - Collision only occurs if the other actor's layer matches bits in this mask

    Example:

    // Actor collides with enemies and obstacles\nactor->mask = pixelroot32::physics::DefaultLayers::kEnemy | \n              pixelroot32::physics::DefaultLayers::kObstacle;\n

    "},{"location":"api_reference/core/actor/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/actor/#void-setcollisionlayercollisionlayer-l","title":"void setCollisionLayer(CollisionLayer l)","text":"

    Sets the collision layer for this actor.

    Parameters: - l (pixelroot32::physics::CollisionLayer): The layer to set

    Returns: - void

    Notes: - Equivalent to setting layer directly - Use bit flags for multiple layers

    Example:

    actor->setCollisionLayer(pixelroot32::physics::DefaultLayers::kPlayer);\n

    "},{"location":"api_reference/core/actor/#void-setcollisionmaskcollisionlayer-m","title":"void setCollisionMask(CollisionLayer m)","text":"

    Sets the collision mask for this actor.

    Parameters: - m (pixelroot32::physics::CollisionLayer): The mask to set

    Returns: - void

    Notes: - Equivalent to setting mask directly - Use bit flags for multiple layers

    Example:

    actor->setCollisionMask(pixelroot32::physics::DefaultLayers::kEnemy | \n                        pixelroot32::physics::DefaultLayers::kObstacle);\n

    "},{"location":"api_reference/core/actor/#bool-isinlayeruint16_t-targetlayer-const","title":"bool isInLayer(uint16_t targetLayer) const","text":"

    Checks if the Actor belongs to a specific collision layer.

    Parameters: - targetLayer (uint16_t): The bit(s) to check (e.g., DefaultLayers::kPlayer)

    Returns: - bool: true if the bit is set in the actor's layer

    Notes: - Uses bitwise AND operation - Useful for checking if an actor is on a specific layer

    Example:

    if (actor->isInLayer(pixelroot32::physics::DefaultLayers::kPlayer)) {\n    // This is a player actor\n}\n

    "},{"location":"api_reference/core/actor/#virtual-rect-gethitbox-0","title":"virtual Rect getHitBox() = 0","text":"

    Gets the hitbox for collision detection. Must be implemented by derived classes.

    Returns: - Rect: A rectangle representing the collision bounds

    Notes: - Called by the collision system to check collisions - Should return the actual collision bounds (may differ from visual size) - Use AABB (Axis-Aligned Bounding Box) for efficiency

    Example:

    Rect getHitBox() override {\n    // Return collision bounds (may be smaller than visual)\n    return {x + 2, y + 2, width - 4, height - 4};\n}\n

    "},{"location":"api_reference/core/actor/#virtual-void-oncollisionactor-other-0","title":"virtual void onCollision(Actor* other) = 0","text":"

    Callback invoked when a collision occurs. Must be implemented by derived classes.

    Parameters: - other (Actor*): The actor that this actor collided with

    Notes: - Called automatically by the collision system when a collision is detected - Both actors' onCollision() methods are called - Use to handle collision responses (damage, bouncing, etc.)

    Example:

    void onCollision(Actor* other) override {\n    // Check what we collided with\n    if (other->isInLayer(pixelroot32::physics::DefaultLayers::kEnemy)) {\n        // Take damage\n        health--;\n        if (health <= 0) {\n            isEnabled = false;\n        }\n    } else if (other->isInLayer(pixelroot32::physics::DefaultLayers::kCollectible)) {\n        // Collect item\n        score += 10;\n        other->isEnabled = false;  // Remove collectible\n    }\n}\n

    "},{"location":"api_reference/core/actor/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the actor logic. Default implementation does nothing.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    Notes: - Override to implement actor-specific update logic - Called automatically by Scene if isEnabled is true - Use deltaTime for frame-rate independent movement

    Example:

    void update(unsigned long deltaTime) override {\n    Actor::update(deltaTime);  // Call base implementation\n\n    // Move actor\n    float speed = 100.0f;  // pixels per second\n    x += (speed * deltaTime) / 1000.0f;\n}\n

    "},{"location":"api_reference/core/actor/#collision-layers","title":"Collision Layers","text":"

    Collision layers use bit flags to organize actors into groups. Common layers:

    • DefaultLayers::kNone (0): No layer
    • DefaultLayers::kPlayer (1 << 0): Player actors
    • DefaultLayers::kEnemy (1 << 1): Enemy actors
    • DefaultLayers::kObstacle (1 << 2): Obstacles/walls
    • DefaultLayers::kProjectile (1 << 3): Projectiles
    • DefaultLayers::kCollectible (1 << 4): Collectible items

    Example:

    // Player collides with enemies and obstacles\nplayer->layer = DefaultLayers::kPlayer;\nplayer->mask = DefaultLayers::kEnemy | DefaultLayers::kObstacle;\n\n// Enemy collides with player and obstacles\nenemy->layer = DefaultLayers::kEnemy;\nenemy->mask = DefaultLayers::kPlayer | DefaultLayers::kObstacle;\n\n// Projectile collides with enemies\nprojectile->layer = DefaultLayers::kProjectile;\nprojectile->mask = DefaultLayers::kEnemy;\n

    "},{"location":"api_reference/core/actor/#usage-example","title":"Usage Example","text":"
    #include \"core/Actor.h\"\n#include \"physics/CollisionTypes.h\"\n\nclass EnemyActor : public pixelroot32::core::Actor {\nprivate:\n    const pixelroot32::graphics::Sprite* sprite;\n    int health = 3;\n\npublic:\n    EnemyActor(float x, float y) \n        : Actor(x, y, 16, 16),\n          sprite(&enemySprite) {\n        // Set collision layer and mask\n        layer = pixelroot32::physics::DefaultLayers::kEnemy;\n        mask = pixelroot32::physics::DefaultLayers::kPlayer | \n               pixelroot32::physics::DefaultLayers::kProjectile;\n    }\n\n    void update(unsigned long deltaTime) override {\n        Actor::update(deltaTime);\n\n        // Move towards player\n        float speed = 50.0f;\n        // ... movement logic\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawSprite(*sprite, \n                           static_cast<int>(x), \n                           static_cast<int>(y), \n                           Color::Red);\n    }\n\n    Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(Actor* other) override {\n        if (other->isInLayer(pixelroot32::physics::DefaultLayers::kProjectile)) {\n            // Hit by projectile\n            health--;\n            if (health <= 0) {\n                isEnabled = false;  // Remove enemy\n            }\n        }\n    }\n};\n
    "},{"location":"api_reference/core/actor/#performance-considerations","title":"Performance Considerations","text":"
    • Collision layers: Use layers efficiently to reduce collision checks
    • Hitbox size: Keep hitboxes simple (AABB) for best performance
    • Collision callbacks: Keep onCollision() fast; avoid expensive operations
    • Layer organization: Group actors by layer to minimize checks
    "},{"location":"api_reference/core/actor/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Collision checks: Collision system automatically optimizes using layers
    • Memory: Each actor consumes memory; stay within MAX_ENTITIES limit
    • Object pooling: Reuse actors instead of creating/destroying frequently
    "},{"location":"api_reference/core/actor/#see-also","title":"See Also","text":"
    • Entity - Base entity class
    • PhysicsActor - Entity with physics
    • CollisionSystem - Collision detection
    • CollisionTypes - Collision layer definitions
    • Manual - Scenes and Entities
    • Manual - Physics and Collisions
    • API Overview
    "},{"location":"api_reference/core/engine/","title":"Engine","text":"

    The main engine class that manages the game loop and core subsystems.

    "},{"location":"api_reference/core/engine/#description","title":"Description","text":"

    Engine acts as the central hub of the PixelRoot32 game engine. It initializes and manages the Renderer, InputManager, AudioEngine, and SceneManager. It runs the main game loop, handling timing (delta time), updating the current scene, and rendering frames.

    The engine provides a unified interface for both ESP32 and Native (SDL2) platforms, abstracting platform-specific details while maintaining consistent behavior.

    "},{"location":"api_reference/core/engine/#namespace","title":"Namespace","text":"
    namespace pixelroot32::core {\n    class Engine {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/engine/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Application entry point (main.cpp)
    "},{"location":"api_reference/core/engine/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/engine/#engineconst-displayconfig-displayconfig-const-inputconfig-inputconfig-const-audioconfig-audioconfig","title":"Engine(const DisplayConfig& displayConfig, const InputConfig& inputConfig, const AudioConfig& audioConfig)","text":"

    Creates a new engine instance with custom display, input, and audio configurations.

    Parameters: - displayConfig (const pixelroot32::graphics::DisplayConfig&): Configuration settings for the display (width, height, rotation, etc.) - inputConfig (const pixelroot32::input::InputConfig&): Configuration settings for the input system (pins, buttons) - audioConfig (const pixelroot32::audio::AudioConfig&): Configuration settings for the audio system (backend, sample rate, buffer size)

    Example:

    #include \"core/Engine.h\"\n#include \"graphics/DisplayConfig.h\"\n#include \"input/InputConfig.h\"\n#include \"audio/AudioConfig.h\"\n\npixelroot32::graphics::DisplayConfig displayConfig;\ndisplayConfig.width = 128;\ndisplayConfig.height = 128;\n\npixelroot32::input::InputConfig inputConfig;\n// Configure input pins...\n\npixelroot32::audio::AudioConfig audioConfig;\naudioConfig.backend = pixelroot32::audio::AudioConfig::Backend::ESP32_DAC;\naudioConfig.sampleRate = 11025;\n\npixelroot32::core::Engine engine(displayConfig, inputConfig, audioConfig);\nengine.init();\nengine.run();\n

    "},{"location":"api_reference/core/engine/#engineconst-displayconfig-displayconfig-const-inputconfig-inputconfig","title":"Engine(const DisplayConfig& displayConfig, const InputConfig& inputConfig)","text":"

    Creates a new engine instance with custom display and input configurations, using default audio settings.

    Parameters: - displayConfig (const pixelroot32::graphics::DisplayConfig&): Configuration settings for the display - inputConfig (const pixelroot32::input::InputConfig&): Configuration settings for the input system

    Example:

    pixelroot32::core::Engine engine(displayConfig, inputConfig);\nengine.init();\nengine.run();\n

    "},{"location":"api_reference/core/engine/#engineconst-displayconfig-displayconfig","title":"Engine(const DisplayConfig& displayConfig)","text":"

    Creates a new engine instance with custom display configuration and default input/audio settings.

    Parameters: - displayConfig (const pixelroot32::graphics::DisplayConfig&): Configuration settings for the display

    Example:

    pixelroot32::core::Engine engine(displayConfig);\nengine.init();\nengine.run();\n

    "},{"location":"api_reference/core/engine/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/engine/#void-init","title":"void init()","text":"

    Initializes the engine subsystems. This method must be called before run().

    Returns: - void

    Notes: - Initializes the Renderer, InputManager, and sets up the initial state - Must be called after construction and before run() - Safe to call multiple times (idempotent)

    Example:

    Engine engine(displayConfig);\nengine.init();  // Initialize subsystems\nengine.setScene(myScene);\nengine.run();   // Start game loop\n

    "},{"location":"api_reference/core/engine/#void-run","title":"void run()","text":"

    Starts the main game loop. This method contains the infinite loop that calls update() and draw() repeatedly until the application exits.

    Returns: - void

    Notes: - This method blocks until the application exits - Handles frame timing and delta time calculation automatically - Calls update() and draw() once per frame - Do not call this method multiple times

    Example:

    Engine engine(displayConfig);\nengine.init();\nengine.setScene(myScene);\nengine.run();  // Blocks here, runs game loop\n

    "},{"location":"api_reference/core/engine/#unsigned-long-getdeltatime-const","title":"unsigned long getDeltaTime() const","text":"

    Gets the time elapsed since the last frame.

    Returns: - unsigned long: The delta time in milliseconds

    Performance Notes: - Very fast (inline accessor) - Safe to call every frame - Use this value to make movement frame-rate independent

    Example:

    void update(unsigned long deltaTime) override {\n    auto& engine = getEngine();\n    unsigned long dt = engine.getDeltaTime();\n\n    // Move at constant speed regardless of FPS\n    float speed = 100.0f;  // pixels per second\n    x += (speed * dt) / 1000.0f;\n}\n

    "},{"location":"api_reference/core/engine/#void-setscenescene-newscene","title":"void setScene(Scene* newScene)","text":"

    Sets the current active scene to be updated and rendered.

    Parameters: - newScene (Scene*): Pointer to the new Scene to become active. Can be nullptr to clear the current scene.

    Notes: - The previous scene is replaced (not pushed onto a stack) - Use SceneManager for push/pop operations if needed - The scene's init() method will be called automatically - Safe to call during the game loop

    Example:

    class MainMenuScene : public pixelroot32::core::Scene {\n    // ...\n};\n\nclass GameScene : public pixelroot32::core::Scene {\n    // ...\n};\n\nMainMenuScene menuScene;\nGameScene gameScene;\n\nEngine engine(displayConfig);\nengine.init();\nengine.setScene(&menuScene);  // Start with menu\nengine.run();\n

    "},{"location":"api_reference/core/engine/#scene-getcurrentscene-const","title":"Scene* getCurrentScene() const","text":"

    Retrieves the currently active scene.

    Returns: - Scene*: Pointer to the current Scene, or nullptr if none is set

    Example:

    auto* currentScene = engine.getCurrentScene();\nif (currentScene) {\n    // Scene is active\n}\n

    "},{"location":"api_reference/core/engine/#void-setrendererrenderer-newrenderer","title":"void setRenderer(Renderer& newRenderer)","text":"

    Replaces the current renderer instance.

    Parameters: - newRenderer (pixelroot32::graphics::Renderer&): Reference to the new Renderer to use

    Notes: - Advanced usage: typically not needed unless implementing custom renderer - The renderer must be properly initialized before use - Use with caution: may break existing rendering code

    "},{"location":"api_reference/core/engine/#renderer-getrenderer","title":"Renderer& getRenderer()","text":"

    Provides access to the Renderer subsystem.

    Returns: - pixelroot32::graphics::Renderer&: Reference to the current Renderer

    Example:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    auto& engineRenderer = engine.getRenderer();\n    engineRenderer.drawSprite(mySprite, 100, 100, Color::White);\n}\n

    "},{"location":"api_reference/core/engine/#inputmanager-getinputmanager","title":"InputManager& getInputManager()","text":"

    Provides access to the InputManager subsystem.

    Returns: - pixelroot32::input::InputManager&: Reference to the InputManager

    Example:

    void update(unsigned long deltaTime) override {\n    auto& input = engine.getInputManager();\n    if (input.isButtonPressed(Buttons::A)) {\n        // Handle button press\n    }\n}\n

    "},{"location":"api_reference/core/engine/#audioengine-getaudioengine","title":"AudioEngine& getAudioEngine()","text":"

    Provides access to the AudioEngine subsystem.

    Returns: - pixelroot32::audio::AudioEngine&: Reference to the AudioEngine

    Example:

    void playSound() {\n    auto& audio = engine.getAudioEngine();\n    pixelroot32::audio::AudioEvent sound{};\n    sound.type = pixelroot32::audio::WaveType::PULSE;\n    sound.frequency = 800.0f;\n    sound.duration = 0.1f;\n    audio.playEvent(sound);\n}\n

    "},{"location":"api_reference/core/engine/#musicplayer-getmusicplayer","title":"MusicPlayer& getMusicPlayer()","text":"

    Provides access to the MusicPlayer subsystem.

    Returns: - pixelroot32::audio::MusicPlayer&: Reference to the MusicPlayer

    Example:

    void playMusic() {\n    auto& music = engine.getMusicPlayer();\n    music.playTrack(myMusicTrack);\n}\n

    "},{"location":"api_reference/core/engine/#usage-example","title":"Usage Example","text":"
    #include \"core/Engine.h\"\n#include \"graphics/DisplayConfig.h\"\n#include \"MyScene.h\"\n\nvoid setup() {\n    // Configure display\n    pixelroot32::graphics::DisplayConfig displayConfig;\n    displayConfig.width = 128;\n    displayConfig.height = 128;\n    displayConfig.rotation = 0;\n\n    // Create engine\n    pixelroot32::core::Engine engine(displayConfig);\n\n    // Initialize\n    engine.init();\n\n    // Create and set scene\n    MyScene myScene;\n    engine.setScene(&myScene);\n\n    // Run game loop\n    engine.run();\n}\n
    "},{"location":"api_reference/core/engine/#performance-considerations","title":"Performance Considerations","text":"
    • Initialization: init() should be called once at startup, not in the game loop
    • Scene switching: Switching scenes is fast but avoid doing it every frame
    • Subsystem access: Getters are inline and very fast; safe to call every frame
    • Delta time: Use getDeltaTime() for frame-rate independent movement
    "},{"location":"api_reference/core/engine/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Ensure init() completes before run() to avoid initialization issues
    • Monitor memory usage when switching scenes frequently
    • Use getDeltaTime() for consistent gameplay across different frame rates
    "},{"location":"api_reference/core/engine/#see-also","title":"See Also","text":"
    • Scene - Scene management
    • Renderer - Rendering system
    • InputManager - Input handling
    • AudioEngine - Audio system
    • Getting Started - Fundamental Concepts
    • Manual - Scenes and Entities
    • API Overview
    "},{"location":"api_reference/core/entity/","title":"Entity","text":"

    Abstract base class for all game objects.

    "},{"location":"api_reference/core/entity/#description","title":"Description","text":"

    Entity is the fundamental building block of the scene. Entities have a position, size, and lifecycle methods (update, draw). All game objects inherit from Entity, including actors, UI elements, and custom game objects.

    Entities are managed by Scene and are automatically updated and drawn each frame when enabled and visible.

    "},{"location":"api_reference/core/entity/#namespace","title":"Namespace","text":"
    namespace pixelroot32::core {\n    class Entity {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/entity/#inheritance","title":"Inheritance","text":"
    • Base class: None (abstract base class)
    • Inherited by: Actor, UI elements, and your custom entity classes
    "},{"location":"api_reference/core/entity/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/entity/#entityfloat-x-float-y-int-w-int-h-entitytype-t","title":"Entity(float x, float y, int w, int h, EntityType t)","text":"

    Creates a new entity with specified position, size, and type.

    Parameters: - x (float): Initial X position in world space - y (float): Initial Y position in world space - w (int): Width in pixels - h (int): Height in pixels - t (EntityType): The type of entity (GENERIC, ACTOR, UI_ELEMENT)

    Example:

    class MyEntity : public pixelroot32::core::Entity {\npublic:\n    MyEntity(float x, float y) \n        : Entity(x, y, 16, 16, EntityType::GENERIC) {}\n\n    void update(unsigned long deltaTime) override {\n        // Update logic\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw logic\n    }\n};\n

    "},{"location":"api_reference/core/entity/#public-properties","title":"Public Properties","text":""},{"location":"api_reference/core/entity/#float-x-y","title":"float x, y","text":"

    Position of the entity in world space.

    Type: float

    Access: Read-write

    Notes: - Position is in world coordinates, not screen coordinates - Can be modified directly or through helper methods - Use for entity positioning and movement

    Example:

    entity->x = 100.0f;\nentity->y = 50.0f;\n

    "},{"location":"api_reference/core/entity/#int-width-height","title":"int width, height","text":"

    Dimensions of the entity in pixels.

    Type: int

    Access: Read-write

    Notes: - Used for collision detection, rendering bounds, and layout - Should match the visual size of the entity - Can be modified at runtime if needed

    Example:

    entity->width = 32;\nentity->height = 32;\n

    "},{"location":"api_reference/core/entity/#entitytype-type","title":"EntityType type","text":"

    The specific type of this entity.

    Type: EntityType enum

    Access: Read-only (set in constructor)

    Values: - EntityType::GENERIC: Generic entity - EntityType::ACTOR: Actor entity (with collision) - EntityType::UI_ELEMENT: UI element

    Notes: - Used for type-safe casting and logic differentiation - Set once in constructor, typically not changed

    "},{"location":"api_reference/core/entity/#bool-isvisible","title":"bool isVisible","text":"

    If false, the entity's draw() method will not be called.

    Type: bool

    Access: Read-write

    Default: true

    Notes: - Use to hide entities without removing them from the scene - More efficient than removing and re-adding entities - Useful for object pooling

    Example:

    entity->isVisible = false;  // Hide entity\nentity->setVisible(true);   // Show entity\n

    "},{"location":"api_reference/core/entity/#bool-isenabled","title":"bool isEnabled","text":"

    If false, the entity's update() method will not be called.

    Type: bool

    Access: Read-write

    Default: true

    Notes: - Use to disable entity logic without removing it - Entity still exists but doesn't update - Useful for paused entities or object pooling

    Example:

    entity->isEnabled = false;  // Disable updates\nentity->setEnabled(true);   // Enable updates\n

    "},{"location":"api_reference/core/entity/#unsigned-char-renderlayer","title":"unsigned char renderLayer","text":"

    The render layer this entity is drawn on.

    Type: unsigned char

    Access: Read-write

    Default: 1

    Notes: - Layers are drawn in ascending order (0 = background, 1 = gameplay, 2 = UI) - Entities on the same layer are drawn in add order - Use to control draw order without changing entity order

    Example:

    entity->renderLayer = 0;  // Background layer\nentity->setRenderLayer(2); // UI layer\n

    "},{"location":"api_reference/core/entity/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/entity/#virtual-void-setvisiblebool-v","title":"virtual void setVisible(bool v)","text":"

    Sets the visibility of the entity.

    Parameters: - v (bool): true to show, false to hide

    Returns: - void

    Notes: - Equivalent to setting isVisible directly - Can be overridden for custom visibility logic

    Example:

    entity->setVisible(false);  // Hide\nentity->setVisible(true);   // Show\n

    "},{"location":"api_reference/core/entity/#virtual-void-setenabledbool-e","title":"virtual void setEnabled(bool e)","text":"

    Sets the enabled state of the entity.

    Parameters: - e (bool): true to enable, false to disable

    Returns: - void

    Notes: - Equivalent to setting isEnabled directly - Can be overridden for custom enable logic

    Example:

    entity->setEnabled(false);  // Disable updates\nentity->setEnabled(true);   // Enable updates\n

    "},{"location":"api_reference/core/entity/#unsigned-char-getrenderlayer-const","title":"unsigned char getRenderLayer() const","text":"

    Gets the current render layer.

    Returns: - unsigned char: The render layer (0-255)

    Example:

    unsigned char layer = entity->getRenderLayer();\n

    "},{"location":"api_reference/core/entity/#virtual-void-setrenderlayerunsigned-char-layer","title":"virtual void setRenderLayer(unsigned char layer)","text":"

    Sets the render layer for this entity.

    Parameters: - layer (unsigned char): The render layer (0 = background, 1 = gameplay, 2 = UI)

    Returns: - void

    Example:

    entity->setRenderLayer(0);  // Background\n

    "},{"location":"api_reference/core/entity/#virtual-void-updateunsigned-long-deltatime-0","title":"virtual void update(unsigned long deltaTime) = 0","text":"

    Updates the entity's logic. Must be implemented by derived classes.

    Parameters: - deltaTime (unsigned long): Time elapsed since the last frame in milliseconds

    Returns: - void

    Notes: - Called automatically by Scene every frame if isEnabled is true - Use deltaTime for frame-rate independent movement - Override to implement entity-specific update logic

    Example:

    void update(unsigned long deltaTime) override {\n    // Move entity\n    float speed = 50.0f;  // pixels per second\n    x += (speed * deltaTime) / 1000.0f;\n\n    // Wrap around screen\n    if (x > 128) {\n        x = 0;\n    }\n}\n

    "},{"location":"api_reference/core/entity/#virtual-void-drawrenderer-renderer-0","title":"virtual void draw(Renderer& renderer) = 0","text":"

    Renders the entity. Must be implemented by derived classes.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer to use for drawing

    Returns: - void

    Notes: - Called automatically by Scene every frame if isVisible is true - Entities are drawn in render layer order, then in add order - Override to implement entity-specific drawing logic

    Example:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Draw sprite at entity position\n    renderer.drawSprite(mySprite, static_cast<int>(x), static_cast<int>(y), Color::White);\n}\n

    "},{"location":"api_reference/core/entity/#entitytype-enum","title":"EntityType Enum","text":"

    Categorizes entities for type-safe casting and logic differentiation.

    Values: - EntityType::GENERIC: Generic entity (default) - EntityType::ACTOR: Actor entity (with collision support) - EntityType::UI_ELEMENT: UI element

    Example:

    if (entity->type == EntityType::ACTOR) {\n    Actor* actor = static_cast<Actor*>(entity);\n    // Use actor-specific methods\n}\n

    "},{"location":"api_reference/core/entity/#rect-structure","title":"Rect Structure","text":"

    Represents a 2D rectangle, typically used for hitboxes or bounds.

    Members: - float x, y: Top-left corner coordinates - int width, height: Dimensions of the rectangle

    Methods: - bool intersects(const Rect& other): Checks if this rectangle intersects with another

    Example:

    pixelroot32::core::Rect rect1{10.0f, 20.0f, 50, 50};\npixelroot32::core::Rect rect2{30.0f, 40.0f, 50, 50};\n\nif (rect1.intersects(rect2)) {\n    // Rectangles overlap\n}\n

    "},{"location":"api_reference/core/entity/#usage-example","title":"Usage Example","text":"
    #include \"core/Entity.h\"\n\nclass Collectible : public pixelroot32::core::Entity {\nprivate:\n    const pixelroot32::graphics::Sprite* sprite;\n\npublic:\n    Collectible(float x, float y) \n        : Entity(x, y, 8, 8, EntityType::GENERIC),\n          sprite(&collectibleSprite) {}\n\n    void update(unsigned long deltaTime) override {\n        // Rotate or animate\n        rotation += deltaTime * 0.001f;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        if (isVisible) {\n            renderer.drawSprite(*sprite, \n                               static_cast<int>(x), \n                               static_cast<int>(y), \n                               Color::Yellow);\n        }\n    }\n\nprivate:\n    float rotation = 0.0f;\n};\n
    "},{"location":"api_reference/core/entity/#performance-considerations","title":"Performance Considerations","text":"
    • Visibility: Use isVisible = false instead of removing entities when hiding
    • Enable state: Use isEnabled = false to pause entity logic
    • Render layers: Organize entities by layer to minimize layer switches
    • Direct access: Direct property access is fast (no function call overhead)
    "},{"location":"api_reference/core/entity/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Each entity consumes memory; stay within MAX_ENTITIES limit
    • Object pooling: Reuse entities instead of creating/destroying frequently
    • Update frequency: Disable entities that don't need to update every frame
    "},{"location":"api_reference/core/entity/#see-also","title":"See Also","text":"
    • Scene - Scene management
    • Actor - Entity with collision support
    • PhysicsActor - Entity with physics
    • Manual - Scenes and Entities
    • API Overview
    "},{"location":"api_reference/core/input_config/","title":"InputConfig","text":"

    Configuration structure for the InputManager.

    "},{"location":"api_reference/core/input_config/#description","title":"Description","text":"

    InputConfig defines the mapping between logical inputs and physical pins (ESP32) or keyboard keys (Native/SDL2). It uses variadic arguments to allow flexible configuration of any number of inputs.

    The configuration is platform-specific: ESP32 uses GPIO pin numbers, while Native uses SDL keyboard scancodes.

    "},{"location":"api_reference/core/input_config/#namespace","title":"Namespace","text":"
    namespace pixelroot32::input {\n    struct InputConfig {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/input_config/#structure","title":"Structure","text":""},{"location":"api_reference/core/input_config/#int-count","title":"int count","text":"

    Total number of configured inputs.

    Type: int

    Access: Read-write

    Default: 0

    Notes: - Must match the number of arguments provided to constructor - Determines the size of the internal button array

    "},{"location":"api_reference/core/input_config/#int-inputpins-esp32-only","title":"int* inputPins (ESP32 only)","text":"

    Array of GPIO pin numbers for ESP32.

    Type: int*

    Access: Read-write

    Default: nullptr

    Notes: - Only available on ESP32 platform - Array size equals count - Pin numbers correspond to ESP32 GPIO pins - Use nullptr if count is 0

    Example:

    // ESP32: 6 buttons on pins 0, 2, 4, 5, 18, 19\npixelroot32::input::InputConfig config(6, 0, 2, 4, 5, 18, 19);\n// config.inputPins[0] = 0  (Up)\n// config.inputPins[1] = 2  (Down)\n// config.inputPins[2] = 4  (Left)\n// config.inputPins[3] = 5  (Right)\n// config.inputPins[4] = 18 (Button A)\n// config.inputPins[5] = 19 (Button B)\n

    "},{"location":"api_reference/core/input_config/#uint8_t-buttonnames-native-only","title":"uint8_t* buttonNames (Native only)","text":"

    Array of button mappings (scancodes) for Native.

    Type: uint8_t*

    Access: Read-write

    Default: nullptr

    Notes: - Only available on Native platform - Array size equals count - Values are SDL keyboard scancodes - Use nullptr if count is 0

    Example:

    // Native: Map to keyboard keys\n#include <SDL2/SDL.h>\n\npixelroot32::input::InputConfig config(6,\n    SDL_SCANCODE_UP,    // Index 0\n    SDL_SCANCODE_DOWN,  // Index 1\n    SDL_SCANCODE_LEFT,  // Index 2\n    SDL_SCANCODE_RIGHT, // Index 3\n    SDL_SCANCODE_X,     // Index 4 (Button A)\n    SDL_SCANCODE_Z      // Index 5 (Button B)\n);\n

    "},{"location":"api_reference/core/input_config/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/input_config/#inputconfigint-count","title":"InputConfig(int count, ...)","text":"

    Constructs a new InputConfig with variadic arguments.

    Parameters: - count (int): Number of inputs to configure - ... (variadic): Variable arguments list of pins (ESP32) or scancodes (Native)

    Notes: - If count <= 0, configuration is empty (nullptr arrays) - Allocates arrays dynamically based on count - Arguments must match count in number - Platform-specific: ESP32 expects int (GPIO pins), Native expects int (SDL scancodes)

    ESP32 Example:

    // Configure 4 directional buttons\npixelroot32::input::InputConfig config(4, 0, 2, 4, 5);\n// Pin 0 = Up, Pin 2 = Down, Pin 4 = Left, Pin 5 = Right\n\n// Configure 6 buttons (4 directions + 2 action buttons)\npixelroot32::input::InputConfig config(6, 0, 2, 4, 5, 18, 19);\n

    Native Example:

    #include <SDL2/SDL.h>\n\n// Configure 4 directional buttons\npixelroot32::input::InputConfig config(4,\n    SDL_SCANCODE_UP,\n    SDL_SCANCODE_DOWN,\n    SDL_SCANCODE_LEFT,\n    SDL_SCANCODE_RIGHT\n);\n\n// Configure 6 buttons (4 directions + 2 action buttons)\npixelroot32::input::InputConfig config(6,\n    SDL_SCANCODE_UP,\n    SDL_SCANCODE_DOWN,\n    SDL_SCANCODE_LEFT,\n    SDL_SCANCODE_RIGHT,\n    SDL_SCANCODE_X,  // Button A\n    SDL_SCANCODE_Z   // Button B\n);\n

    "},{"location":"api_reference/core/input_config/#usage-example","title":"Usage Example","text":""},{"location":"api_reference/core/input_config/#esp32-configuration","title":"ESP32 Configuration","text":"
    #include \"input/InputConfig.h\"\n#include \"input/InputManager.h\"\n#include \"core/Engine.h\"\n\nvoid setup() {\n    // Configure input: 6 buttons\n    // Pins: Up=0, Down=2, Left=4, Right=5, A=18, B=19\n    pixelroot32::input::InputConfig inputConfig(6, 0, 2, 4, 5, 18, 19);\n\n    // Create input manager\n    pixelroot32::input::InputManager inputManager(inputConfig);\n    inputManager.init();\n\n    // Or use with Engine\n    pixelroot32::graphics::DisplayConfig displayConfig;\n    pixelroot32::core::Engine engine(displayConfig, inputConfig);\n    engine.init();\n    engine.run();\n}\n
    "},{"location":"api_reference/core/input_config/#native-configuration","title":"Native Configuration","text":"
    #include \"input/InputConfig.h\"\n#include <SDL2/SDL.h>\n\nvoid setup() {\n    // Configure input: 6 buttons mapped to keyboard\n    pixelroot32::input::InputConfig inputConfig(6,\n        SDL_SCANCODE_UP,    // Index 0: Up\n        SDL_SCANCODE_DOWN,  // Index 1: Down\n        SDL_SCANCODE_LEFT,  // Index 2: Left\n        SDL_SCANCODE_RIGHT, // Index 3: Right\n        SDL_SCANCODE_X,     // Index 4: Button A\n        SDL_SCANCODE_Z      // Index 5: Button B\n    );\n\n    // Use with Engine\n    pixelroot32::graphics::DisplayConfig displayConfig;\n    pixelroot32::core::Engine engine(displayConfig, inputConfig);\n    engine.init();\n    engine.run();\n}\n
    "},{"location":"api_reference/core/input_config/#platform-agnostic-configuration","title":"Platform-Agnostic Configuration","text":"
    #ifdef PLATFORM_ESP32\n    // ESP32: Use GPIO pins\n    pixelroot32::input::InputConfig inputConfig(6, 0, 2, 4, 5, 18, 19);\n#elif PLATFORM_NATIVE\n    // Native: Use SDL scancodes\n    #include <SDL2/SDL.h>\n    pixelroot32::input::InputConfig inputConfig(6,\n        SDL_SCANCODE_UP,\n        SDL_SCANCODE_DOWN,\n        SDL_SCANCODE_LEFT,\n        SDL_SCANCODE_RIGHT,\n        SDL_SCANCODE_X,\n        SDL_SCANCODE_Z\n    );\n#endif\n
    "},{"location":"api_reference/core/input_config/#button-index-mapping","title":"Button Index Mapping","text":"

    Button indices are determined by the order in the constructor:

    Typical Convention: - Index 0: Up / Primary action - Index 1: Down / Secondary action - Index 2: Left - Index 3: Right - Index 4+: Additional buttons

    Example:

    // 4-button D-pad\nInputConfig config(4, UP_PIN, DOWN_PIN, LEFT_PIN, RIGHT_PIN);\n// Index 0 = Up, Index 1 = Down, Index 2 = Left, Index 3 = Right\n\n// 6-button setup (D-pad + 2 action buttons)\nInputConfig config(6, UP_PIN, DOWN_PIN, LEFT_PIN, RIGHT_PIN, A_PIN, B_PIN);\n// Index 0-3 = D-pad, Index 4 = A, Index 5 = B\n

    "},{"location":"api_reference/core/input_config/#esp32-pin-considerations","title":"ESP32 Pin Considerations","text":"
    • GPIO pins: Use any available GPIO pin
    • Pull-up/pull-down: Configure resistors appropriately
    • Input mode: Pins are automatically configured as inputs
    • Restrictions: Some pins have special functions (check ESP32 datasheet)

    Common Pin Choices: - GPIO 0, 2, 4, 5: Safe for buttons (watch for boot mode pins) - GPIO 18, 19: Good for additional buttons - Avoid: GPIO 6-11 (flash), GPIO 34-39 (input only, no pull-up)

    "},{"location":"api_reference/core/input_config/#native-sdl-scancode-reference","title":"Native SDL Scancode Reference","text":"

    Common SDL scancodes:

    • SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT: Arrow keys
    • SDL_SCANCODE_W, SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_D: WASD
    • SDL_SCANCODE_X, SDL_SCANCODE_Z: Common action buttons
    • SDL_SCANCODE_SPACE: Spacebar
    • SDL_SCANCODE_RETURN: Enter key

    Example:

    // WASD + Space + Enter\npixelroot32::input::InputConfig config(6,\n    SDL_SCANCODE_W,        // Up\n    SDL_SCANCODE_S,        // Down\n    SDL_SCANCODE_A,        // Left\n    SDL_SCANCODE_D,         // Right\n    SDL_SCANCODE_SPACE,    // Jump\n    SDL_SCANCODE_RETURN    // Action\n);\n

    "},{"location":"api_reference/core/input_config/#performance-considerations","title":"Performance Considerations","text":"
    • Memory: Arrays are allocated dynamically (small overhead)
    • Configuration: Done once at startup, no runtime cost
    • Access: Button indices are fast (array access)
    "},{"location":"api_reference/core/input_config/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Pin configuration: Ensure pins are not used by other peripherals
    • Debouncing: Hardware debouncing recommended for reliable input
    • Power: Buttons should use pull-up resistors to avoid floating pins
    "},{"location":"api_reference/core/input_config/#see-also","title":"See Also","text":"
    • InputManager - Input handling
    • Engine - Engine that uses InputConfig
    • Manual - Input and Control
    • API Overview
    "},{"location":"api_reference/core/input_manager/","title":"InputManager","text":"

    Handles input from physical buttons or keyboard (on PC).

    "},{"location":"api_reference/core/input_manager/#description","title":"Description","text":"

    The InputManager polls configured pins (ESP32) or keyboard state (Native), handles debouncing, and tracks button states (Pressed, Released, Down, Clicked). It provides a unified input interface for both platforms.

    The manager supports edge detection (just pressed/released) and continuous state (held down), making it suitable for both gameplay and UI navigation.

    "},{"location":"api_reference/core/input_manager/#namespace","title":"Namespace","text":"
    namespace pixelroot32::input {\n    class InputManager {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/input_manager/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Engine (manages input manager instance)
    "},{"location":"api_reference/core/input_manager/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/input_manager/#inputmanagerconst-inputconfig-config","title":"InputManager(const InputConfig& config)","text":"

    Constructs the InputManager with a specific configuration.

    Parameters: - config (const InputConfig&): The input configuration (pins, button count)

    Example:

    #include \"input/InputManager.h\"\n#include \"input/InputConfig.h\"\n\n// ESP32: Configure GPIO pins\npixelroot32::input::InputConfig inputConfig(6, 0, 2, 4, 5, 18, 19);\npixelroot32::input::InputManager inputManager(inputConfig);\ninputManager.init();\n\n// Native: Configure keyboard keys\n// (Configuration handled differently on Native)\n

    "},{"location":"api_reference/core/input_manager/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/input_manager/#void-init","title":"void init()","text":"

    Initializes the input pins.

    Returns: - void

    Notes: - Must be called after construction and before use - Configures GPIO pins (ESP32) or keyboard state (Native) - Safe to call multiple times (idempotent) - Typically called automatically by Engine::init()

    Example:

    InputManager inputManager(inputConfig);\ninputManager.init();  // Initialize before use\n

    "},{"location":"api_reference/core/input_manager/#void-updateunsigned-long-dt","title":"void update(unsigned long dt)","text":"

    Updates input state by polling hardware pins (ESP32) or keyboard state (Native).

    Parameters: - dt (unsigned long): Delta time in milliseconds

    Returns: - void

    Notes: - Must be called every frame for proper input detection - Handles debouncing automatically - Updates button states and edge detection - Typically called automatically by Engine::update()

    ESP32 Example:

    void update(unsigned long deltaTime) override {\n    // Input is updated automatically by Engine\n    // Access input via engine.getInputManager()\n}\n

    Native Example:

    // On Native, update is called with keyboard state:\nvoid update(unsigned long dt, const uint8_t* keyboardState);\n

    "},{"location":"api_reference/core/input_manager/#bool-isbuttonpresseduint8_t-buttonindex-const","title":"bool isButtonPressed(uint8_t buttonIndex) const","text":"

    Checks if a button was just pressed this frame.

    Parameters: - buttonIndex (uint8_t): Index of the button to check (0-based)

    Returns: - bool: true if the button transitioned from UP to DOWN this frame

    Notes: - Returns true only on the frame the button was pressed - Useful for one-time actions (jump, shoot, menu select) - Resets automatically on next frame

    Example:

    auto& input = engine.getInputManager();\n\nif (input.isButtonPressed(0)) {  // Button A (index 0)\n    // Jump (only once per press)\n    player->jump();\n}\n\nif (input.isButtonPressed(1)) {  // Button B (index 1)\n    // Shoot (only once per press)\n    player->shoot();\n}\n

    "},{"location":"api_reference/core/input_manager/#bool-isbuttonreleaseduint8_t-buttonindex-const","title":"bool isButtonReleased(uint8_t buttonIndex) const","text":"

    Checks if a button was just released this frame.

    Parameters: - buttonIndex (uint8_t): Index of the button to check

    Returns: - bool: true if the button transitioned from DOWN to UP this frame

    Notes: - Returns true only on the frame the button was released - Useful for detecting button release events - Less commonly used than isButtonPressed()

    Example:

    auto& input = engine.getInputManager();\n\nif (input.isButtonReleased(0)) {\n    // Button A was just released\n    player->stopCharging();\n}\n

    "},{"location":"api_reference/core/input_manager/#bool-isbuttonclickeduint8_t-buttonindex-const","title":"bool isButtonClicked(uint8_t buttonIndex) const","text":"

    Checks if a button was clicked (pressed and released).

    Parameters: - buttonIndex (uint8_t): Index of the button to check

    Returns: - bool: true if the button was clicked (pressed then released)

    Notes: - Returns true when button is released after being pressed - Useful for UI buttons and menu selection - Detects complete press-release cycle

    Example:

    auto& input = engine.getInputManager();\n\nif (input.isButtonClicked(0)) {  // Button A clicked\n    // Select menu item\n    menu->select();\n}\n

    "},{"location":"api_reference/core/input_manager/#bool-isbuttondownuint8_t-buttonindex-const","title":"bool isButtonDown(uint8_t buttonIndex) const","text":"

    Checks if a button is currently held down.

    Parameters: - buttonIndex (uint8_t): Index of the button to check

    Returns: - bool: true if the button is currently in the DOWN state

    Notes: - Returns true for as long as the button is held - Useful for continuous actions (movement, charging) - Use with deltaTime for frame-rate independent movement

    Example:

    auto& input = engine.getInputManager();\n\nfloat speed = 100.0f;  // pixels per second\nfloat vx = 0.0f, vy = 0.0f;\n\nif (input.isButtonDown(2)) {  // Left button\n    vx = -speed;\n}\nif (input.isButtonDown(3)) {  // Right button\n    vx = speed;\n}\nif (input.isButtonDown(0)) {  // Up button\n    vy = -speed;\n}\nif (input.isButtonDown(1)) {  // Down button\n    vy = speed;\n}\n\n// Apply movement (frame-rate independent)\nx += (vx * deltaTime) / 1000.0f;\ny += (vy * deltaTime) / 1000.0f;\n

    "},{"location":"api_reference/core/input_manager/#button-indices","title":"Button Indices","text":"

    Button indices are defined by the order in InputConfig:

    Typical Mapping: - 0: Up / Button A - 1: Down / Button B - 2: Left - 3: Right - 4: Additional button 1 - 5: Additional button 2

    Example:

    // Configure 6 buttons: Up, Down, Left, Right, A, B\npixelroot32::input::InputConfig inputConfig(6, \n    GPIO_UP,    // Index 0\n    GPIO_DOWN,  // Index 1\n    GPIO_LEFT,  // Index 2\n    GPIO_RIGHT, // Index 3\n    GPIO_A,     // Index 4\n    GPIO_B      // Index 5\n);\n\n// Use indices\nif (input.isButtonDown(2)) {  // Left\n    moveLeft();\n}\nif (input.isButtonPressed(4)) {  // A button\n    jump();\n}\n

    "},{"location":"api_reference/core/input_manager/#usage-example","title":"Usage Example","text":"
    #include \"input/InputManager.h\"\n#include \"core/Engine.h\"\n\nclass PlayerController {\nprivate:\n    pixelroot32::core::Engine& engine;\n\npublic:\n    PlayerController(pixelroot32::core::Engine& eng) : engine(eng) {}\n\n    void update(unsigned long deltaTime) {\n        auto& input = engine.getInputManager();\n\n        // Movement (continuous)\n        float speed = 150.0f;  // pixels per second\n        float vx = 0.0f, vy = 0.0f;\n\n        if (input.isButtonDown(3)) {  // Right\n            vx = speed;\n        }\n        if (input.isButtonDown(2)) {  // Left\n            vx = -speed;\n        }\n        if (input.isButtonDown(0)) {  // Up\n            vy = -speed;\n        }\n        if (input.isButtonDown(1)) {  // Down\n            vy = speed;\n        }\n\n        // Apply movement\n        playerX += (vx * deltaTime) / 1000.0f;\n        playerY += (vy * deltaTime) / 1000.0f;\n\n        // Actions (one-time)\n        if (input.isButtonPressed(4)) {  // A button\n            player->jump();\n        }\n\n        if (input.isButtonPressed(5)) {  // B button\n            player->shoot();\n        }\n    }\n};\n
    "},{"location":"api_reference/core/input_manager/#input-state-comparison","title":"Input State Comparison","text":"Method Returns true when Use Case isButtonPressed() Button just pressed this frame One-time actions (jump, shoot) isButtonReleased() Button just released this frame Release events (stop charging) isButtonClicked() Button pressed then released UI buttons, menu selection isButtonDown() Button currently held Continuous actions (movement)"},{"location":"api_reference/core/input_manager/#performance-considerations","title":"Performance Considerations","text":"
    • Update frequency: update() must be called every frame
    • Debouncing: Handled automatically, no performance impact
    • State queries: All query methods are fast (inline accessors)
    • Memory: Button state arrays are small and efficient
    "},{"location":"api_reference/core/input_manager/#esp32-considerations","title":"ESP32 Considerations","text":"
    • GPIO pins: Configure pins in InputConfig
    • Pull-up/pull-down: Ensure proper resistor configuration
    • Debouncing: Hardware debouncing recommended for noisy buttons
    • Pin limits: Some ESP32 pins have restrictions (check datasheet)
    "},{"location":"api_reference/core/input_manager/#native-considerations","title":"Native Considerations","text":"
    • Keyboard mapping: Uses SDL scancodes
    • Key detection: Automatically handles keyboard state
    • Multiple keys: Can detect multiple keys simultaneously
    "},{"location":"api_reference/core/input_manager/#see-also","title":"See Also","text":"
    • InputConfig - Input configuration
    • Engine - Engine that manages InputManager
    • Manual - Input and Control
    • API Overview
    "},{"location":"api_reference/core/physics_actor/","title":"PhysicsActor","text":"

    An actor with basic 2D physics properties.

    "},{"location":"api_reference/core/physics_actor/#description","title":"Description","text":"

    PhysicsActor extends the base Actor class by adding velocity, acceleration, friction, restitution (bounciness), and world boundary collision resolution. It is designed for objects that need to move and bounce within a defined area, such as balls, projectiles, or platformer characters.

    PhysicsActor automatically handles: - Velocity-based movement - Friction application - World boundary collision and bouncing - Collision callbacks

    "},{"location":"api_reference/core/physics_actor/#namespace","title":"Namespace","text":"
    namespace pixelroot32::core {\n    class PhysicsActor : public Actor {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/physics_actor/#inheritance","title":"Inheritance","text":"
    • Inherits from: Actor
    • Inherited by: Your custom physics-enabled actor classes
    "},{"location":"api_reference/core/physics_actor/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/physics_actor/#physicsactorfloat-x-float-y-float-w-float-h","title":"PhysicsActor(float x, float y, float w, float h)","text":"

    Creates a physics-enabled actor with specified position and size.

    Parameters: - x (float): Initial X position in world space - y (float): Initial Y position in world space - w (float): Actor width in pixels - h (float): Actor height in pixels

    Notes: - Velocity starts at (0, 0) - Restitution defaults to 1.0 (perfect bounce) - Friction defaults to 0.0 (no friction) - No world limits by default

    Example:

    class BallActor : public pixelroot32::core::PhysicsActor {\npublic:\n    BallActor(float x, float y) \n        : PhysicsActor(x, y, 8.0f, 8.0f) {\n        // Set physics properties\n        setRestitution(0.8f);  // 80% bounce\n        setFriction(0.1f);     // Small friction\n        setWorldSize(128, 128); // World bounds\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledCircle(static_cast<int>(x + width/2), \n                                 static_cast<int>(y + height/2), \n                                 width/2, \n                                 Color::White);\n    }\n\n    Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(Actor* other) override {\n        // Bounce off other actors\n    }\n\n    void onWorldCollision() override {\n        // Play bounce sound\n    }\n};\n

    "},{"location":"api_reference/core/physics_actor/#protected-properties","title":"Protected Properties","text":""},{"location":"api_reference/core/physics_actor/#float-vx-vy","title":"float vx, vy","text":"

    Horizontal and vertical velocity components.

    Type: float

    Access: Protected (use setVelocity() to modify)

    Default: 0.0f

    Notes: - Velocity is in pixels per second - Automatically applied during update() - Modified by friction and world collisions

    "},{"location":"api_reference/core/physics_actor/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/physics_actor/#void-setvelocityfloat-x-float-y","title":"void setVelocity(float x, float y)","text":"

    Sets the linear velocity of the actor.

    Parameters: - x (float): Horizontal velocity in pixels per second - y (float): Vertical velocity in pixels per second

    Returns: - void

    Notes: - Velocity is applied every frame during update() - Use for initial velocity or impulse-based movement - Can be called every frame for continuous control

    Example:

    // Set initial velocity\nphysicsActor->setVelocity(100.0f, -200.0f);  // Move right and up\n\n// Continuous control (e.g., player movement)\nvoid update(unsigned long deltaTime) override {\n    PhysicsActor::update(deltaTime);\n\n    float speed = 150.0f;\n    float vx = 0.0f, vy = 0.0f;\n\n    if (input.isButtonDown(Buttons::LEFT)) vx = -speed;\n    if (input.isButtonDown(Buttons::RIGHT)) vx = speed;\n    if (input.isButtonDown(Buttons::UP)) vy = -speed;\n    if (input.isButtonDown(Buttons::DOWN)) vy = speed;\n\n    setVelocity(vx, vy);\n}\n

    "},{"location":"api_reference/core/physics_actor/#void-setrestitutionfloat-r","title":"void setRestitution(float r)","text":"

    Sets the restitution (bounciness) of the actor.

    Parameters: - r (float): Restitution value (0.0 to 1.0+) - 0.0: No bounce (stops on impact) - 1.0: Perfect bounce (no energy loss) - > 1.0: Energy gain (unrealistic but possible)

    Returns: - void

    Notes: - Applied when actor collides with world boundaries - Higher values = more bouncy - Typical values: 0.5-0.9 for realistic bouncing

    Example:

    ball->setRestitution(0.8f);  // 80% bounce\n

    "},{"location":"api_reference/core/physics_actor/#void-setfrictionfloat-f","title":"void setFriction(float f)","text":"

    Sets the friction coefficient.

    Parameters: - f (float): Friction value - 0.0: No friction (object continues moving) - > 0.0: Friction applied to velocity each frame

    Returns: - void

    Notes: - Applied every frame to reduce velocity - Higher values = more friction (slower movement) - Typical values: 0.05-0.2 for smooth deceleration

    Example:

    player->setFriction(0.1f);  // Light friction\n

    "},{"location":"api_reference/core/physics_actor/#void-setlimitslimitrect-limits","title":"void setLimits(LimitRect limits)","text":"

    Sets custom movement limits for the actor.

    Parameters: - limits (LimitRect): A rectangle defining the allowed area

    Returns: - void

    Notes: - Overrides world size limits - Use -1 for any boundary to disable that limit - Actor will bounce off these boundaries

    Example:

    pixelroot32::core::LimitRect limits;\nlimits.left = 0;\nlimits.top = 0;\nlimits.right = 128;\nlimits.bottom = 128;\nphysicsActor->setLimits(limits);\n

    "},{"location":"api_reference/core/physics_actor/#void-setworldsizeint-width-int-height","title":"void setWorldSize(int width, int height)","text":"

    Defines the world size for boundary checking.

    Parameters: - width (int): Width of the world in pixels - height (int): Height of the world in pixels

    Returns: - void

    Notes: - Used as default limits if no custom LimitRect is provided - Actor will bounce off world boundaries - Set to display size for screen boundaries

    Example:

    physicsActor->setWorldSize(128, 128);  // Match display size\n

    "},{"location":"api_reference/core/physics_actor/#worldcollisioninfo-getworldcollisioninfo-const","title":"WorldCollisionInfo getWorldCollisionInfo() const","text":"

    Gets information about collisions with the world boundaries.

    Returns: - WorldCollisionInfo: A struct containing collision flags (left, right, top, bottom)

    Notes: - Updated every frame during update() - Use to detect which boundary was hit - Useful for sound effects or special behaviors

    Example:

    void update(unsigned long deltaTime) override {\n    PhysicsActor::update(deltaTime);\n\n    auto collision = getWorldCollisionInfo();\n    if (collision.left || collision.right) {\n        // Hit side wall\n        playSound(wallHitSound);\n    }\n    if (collision.top || collision.bottom) {\n        // Hit top or bottom\n        playSound(ceilingHitSound);\n    }\n}\n

    "},{"location":"api_reference/core/physics_actor/#virtual-void-oncollisionactor-other-override","title":"virtual void onCollision(Actor* other) override","text":"

    Callback triggered when this actor collides with another actor.

    Parameters: - other (Actor*): Pointer to the actor involved in the collision

    Returns: - void

    Notes: - Called automatically by the collision system - Override to implement custom collision responses - Default implementation does nothing

    Example:

    void onCollision(Actor* other) override {\n    if (other->isInLayer(DefaultLayers::kEnemy)) {\n        // Bounce off enemy\n        vx = -vx * 0.5f;\n        vy = -vy * 0.5f;\n    }\n}\n

    "},{"location":"api_reference/core/physics_actor/#virtual-void-onworldcollision","title":"virtual void onWorldCollision()","text":"

    Callback triggered when this actor collides with world boundaries.

    Returns: - void

    Notes: - Called automatically when a world boundary collision occurs - Override to implement custom behavior (sound effects, particles, etc.) - Default implementation does nothing

    Example:

    void onWorldCollision() override {\n    // Play bounce sound\n    auto& audio = engine.getAudioEngine();\n    pixelroot32::audio::AudioEvent sound{};\n    sound.type = pixelroot32::audio::WaveType::NOISE;\n    sound.frequency = 500.0f;\n    sound.duration = 0.05f;\n    audio.playEvent(sound);\n\n    // Spawn particles\n    spawnBounceParticles();\n}\n

    "},{"location":"api_reference/core/physics_actor/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the actor state. Applies physics integration and checks for world boundary collisions.

    Parameters: - deltaTime (unsigned long): Time elapsed since the last frame in milliseconds

    Returns: - void

    Notes: - Called automatically by Scene if isEnabled is true - Applies velocity to position - Applies friction to velocity - Resolves world boundary collisions - Override to add custom update logic, but call PhysicsActor::update(deltaTime) first

    Example:

    void update(unsigned long deltaTime) override {\n    // Apply physics\n    PhysicsActor::update(deltaTime);\n\n    // Custom logic\n    if (shouldApplyGravity) {\n        vy += gravity * (deltaTime / 1000.0f);\n    }\n}\n

    "},{"location":"api_reference/core/physics_actor/#limitrect-structure","title":"LimitRect Structure","text":"

    Bounding rectangle for world-collision resolution.

    Members: - int left: Left boundary (-1 means no limit) - int top: Top boundary (-1 means no limit) - int right: Right boundary (-1 means no limit) - int bottom: Bottom boundary (-1 means no limit)

    Methods: - int width() const: Calculates width (right - left) - int height() const: Calculates height (bottom - top)

    Example:

    pixelroot32::core::LimitRect limits(10, 10, 118, 118);  // 10px margin\nphysicsActor->setLimits(limits);\n

    "},{"location":"api_reference/core/physics_actor/#worldcollisioninfo-structure","title":"WorldCollisionInfo Structure","text":"

    Information about world collisions in the current frame.

    Members: - bool left: True if collided with the left boundary - bool right: True if collided with the right boundary - bool top: True if collided with the top boundary - bool bottom: True if collided with the bottom boundary

    Example:

    auto collision = physicsActor->getWorldCollisionInfo();\nif (collision.bottom) {\n    // On ground\n    canJump = true;\n}\n

    "},{"location":"api_reference/core/physics_actor/#usage-example","title":"Usage Example","text":"
    #include \"core/PhysicsActor.h\"\n\nclass BouncingBall : public pixelroot32::core::PhysicsActor {\npublic:\n    BouncingBall(float x, float y) \n        : PhysicsActor(x, y, 8.0f, 8.0f) {\n        // Set physics properties\n        setRestitution(0.9f);  // Very bouncy\n        setFriction(0.05f);    // Light friction\n        setWorldSize(128, 128);\n\n        // Set initial velocity\n        setVelocity(100.0f, -150.0f);\n\n        // Set collision layer\n        layer = pixelroot32::physics::DefaultLayers::kProjectile;\n        mask = pixelroot32::physics::DefaultLayers::kObstacle;\n    }\n\n    void update(unsigned long deltaTime) override {\n        PhysicsActor::update(deltaTime);\n\n        // Apply gravity\n        vy += 200.0f * (deltaTime / 1000.0f);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledCircle(static_cast<int>(x + width/2), \n                                 static_cast<int>(y + height/2), \n                                 width/2, \n                                 Color::White);\n    }\n\n    Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(Actor* other) override {\n        // Bounce off obstacles\n        vx = -vx * 0.8f;\n        vy = -vy * 0.8f;\n    }\n\n    void onWorldCollision() override {\n        // Play bounce sound\n        playBounceSound();\n    }\n};\n
    "},{"location":"api_reference/core/physics_actor/#performance-considerations","title":"Performance Considerations","text":"
    • Physics integration: Very efficient (simple velocity integration)
    • World bounds: Boundary checks are fast (AABB)
    • Friction: Applied every frame; keep friction values reasonable
    • Collision callbacks: Keep onCollision() and onWorldCollision() fast
    "},{"location":"api_reference/core/physics_actor/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Floating point: Uses float math; acceptable for ESP32 but integer math would be faster
    • Frame rate: Physics is frame-rate independent (uses deltaTime)
    • Memory: Each PhysicsActor consumes more memory than Actor (velocity, limits, etc.)
    "},{"location":"api_reference/core/physics_actor/#see-also","title":"See Also","text":"
    • Actor - Base actor class
    • CollisionSystem - Collision detection
    • Manual - Physics and Collisions
    • API Overview
    "},{"location":"api_reference/core/scene/","title":"Scene","text":"

    Represents a game level or screen containing entities.

    "},{"location":"api_reference/core/scene/#description","title":"Description","text":"

    A Scene manages a collection of Entities and a CollisionSystem. It is responsible for updating and drawing all entities it contains. Scenes provide lifecycle hooks (init(), update(), draw()) to manage gameplay segments.

    Scenes are the primary organizational unit in PixelRoot32, similar to levels or screens in other game engines. Each scene can contain up to MAX_ENTITIES (default 32; overridable via compiler flags) entities, and drawing uses up to MAX_LAYERS (default 3; overridable) render layers.

    "},{"location":"api_reference/core/scene/#namespace","title":"Namespace","text":"
    namespace pixelroot32::core {\n    class Scene {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/scene/#inheritance","title":"Inheritance","text":"
    • Base class: None (abstract base class)
    • Inherited by: Your custom scene classes (e.g., MainMenuScene, GameScene)
    "},{"location":"api_reference/core/scene/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/scene/#scene_1","title":"Scene()","text":"

    Creates an empty scene ready to be populated with entities.

    Notes: - The scene starts with no entities - init() should be called when the scene becomes active - The collision system is automatically initialized

    Example:

    class MyScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Initialize scene resources\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);  // Update entities and collisions\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        Scene::draw(renderer);  // Draw all entities\n    }\n};\n

    "},{"location":"api_reference/core/scene/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/scene/#virtual-void-init","title":"virtual void init()","text":"

    Initializes the scene. Called when entering the scene.

    Returns: - void

    Notes: - Called automatically when the scene is set via Engine::setScene() - Override this method to initialize scene-specific resources - Safe to call multiple times (idempotent) - Add entities here or in the constructor

    Example:

    class GameScene : public pixelroot32::core::Scene {\nprivate:\n    PlayerActor* player;\n    std::array<EnemyActor*, 10> enemies;\n\npublic:\n    void init() override {\n        // Create player\n        player = new PlayerActor();\n        addEntity(player);\n\n        // Create enemies\n        for (int i = 0; i < 10; i++) {\n            enemies[i] = new EnemyActor();\n            addEntity(enemies[i]);\n        }\n    }\n};\n

    "},{"location":"api_reference/core/scene/#virtual-void-updateunsigned-long-deltatime","title":"virtual void update(unsigned long deltaTime)","text":"

    Updates all entities in the scene and handles collisions.

    Parameters: - deltaTime (unsigned long): Time elapsed since last frame in milliseconds

    Notes: - Called automatically by the engine every frame - Updates all entities in the scene - Processes collisions between actors - Override to add custom update logic, but call Scene::update(deltaTime) to maintain entity updates

    Example:

    void update(unsigned long deltaTime) override {\n    // Custom update logic\n    gameTimer += deltaTime;\n\n    // Update entities and collisions\n    Scene::update(deltaTime);\n\n    // Additional logic after entity updates\n    if (gameTimer > 60000) {\n        // Game over after 60 seconds\n    }\n}\n

    "},{"location":"api_reference/core/scene/#virtual-void-drawrenderer-renderer","title":"virtual void draw(Renderer& renderer)","text":"

    Draws all visible entities in the scene.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): The renderer to use for drawing

    Notes: - Called automatically by the engine every frame after update() - Draws all visible entities in the scene - Override to add custom drawing logic, but call Scene::draw(renderer) to maintain entity rendering - Entities are drawn in the order they were added

    Example:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Draw background\n    renderer.drawTileMap(backgroundTileMap, 0, 0, Color::White);\n\n    // Draw all entities\n    Scene::draw(renderer);\n\n    // Draw UI overlay\n    renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n}\n

    "},{"location":"api_reference/core/scene/#void-addentityentity-entity","title":"void addEntity(Entity* entity)","text":"

    Adds an entity to the scene.

    Parameters: - entity (Entity*): Pointer to the Entity to add. Must not be nullptr.

    Notes: - Entities are added to an internal queue - Maximum of MAX_ENTITIES (default 32; overridable) entities per scene - If the limit is reached, the entity may not be added (check return value if available) - Entities are updated and drawn in the order they were added - The entity's lifetime is managed by the scene (do not delete manually while in scene)

    Example:

    void init() override {\n    // Create and add player\n    PlayerActor* player = new PlayerActor();\n    player->setPosition(64, 64);\n    addEntity(player);\n\n    // Create and add enemy\n    EnemyActor* enemy = new EnemyActor();\n    enemy->setPosition(100, 100);\n    addEntity(enemy);\n}\n

    "},{"location":"api_reference/core/scene/#void-removeentityentity-entity","title":"void removeEntity(Entity* entity)","text":"

    Removes an entity from the scene.

    Parameters: - entity (Entity*): Pointer to the Entity to remove

    Notes: - The entity is removed from the update and draw queues - The entity is not deleted automatically (you must manage its lifetime) - Safe to call even if the entity is not in the scene - Consider using object pooling instead of frequent add/remove

    Example:

    void onEnemyDestroyed(EnemyActor* enemy) {\n    removeEntity(enemy);\n    // Return to pool or delete\n    enemyPool.returnToPool(enemy);\n}\n

    "},{"location":"api_reference/core/scene/#void-clearentities","title":"void clearEntities()","text":"

    Removes all entities from the scene.

    Notes: - All entities are removed from the update and draw queues - Entities are not deleted automatically (you must manage their lifetimes) - Useful for scene cleanup or reset - Consider using object pooling to reuse entities

    Example:

    void reset() {\n    clearEntities();\n    // Return all entities to pool\n    for (auto* entity : entityPool) {\n        entityPool.returnToPool(entity);\n    }\n}\n

    "},{"location":"api_reference/core/scene/#protected-members","title":"Protected Members","text":""},{"location":"api_reference/core/scene/#arduinoqueue-entities","title":"ArduinoQueue entities

    Queue of entities in the scene. Accessible to derived classes for custom entity management.

    Type: ArduinoQueue<Entity*>

    Notes: - Maximum capacity: MAX_ENTITIES (default 32; overridable) - Direct access allows custom iteration or filtering - Use with caution: modifying while iterating may cause issues

    ","text":""},{"location":"api_reference/core/scene/#collisionsystem-collisionsystem","title":"CollisionSystem collisionSystem

    System to handle collisions between actors. Accessible to derived classes for custom collision handling.

    Type: pixelroot32::physics::CollisionSystem

    Notes: - Automatically processes collisions between actors - Uses collision layers and masks for filtering - Can be accessed for custom collision queries

    ","text":""},{"location":"api_reference/core/scene/#overriding-scene-limits-max_layers-max_entities","title":"Overriding scene limits (MAX_LAYERS / MAX_ENTITIES)","text":"

    The engine defines default limits in core/Scene.h: MAX_LAYERS (default 3) and MAX_ENTITIES (default 32). These are guarded with #ifndef, so you can override them from your project without modifying the engine.

    ESP32 platform limitation

    The default of 3 for MAX_LAYERS is due to ESP32 platform constraints (memory and draw-loop cost). On native/PC you can safely use a higher value; on ESP32, increasing it may affect performance or memory.

    "},{"location":"api_reference/core/scene/#option-a-compiler-flags-recommended","title":"Option A: Compiler flags (recommended)

    In your project (e.g. in platformio.ini), add the defines to build_flags for the environment you use:

    build_flags =\n    -DMAX_LAYERS=5\n    -DMAX_ENTITIES=64\n

    The compiler defines MAX_LAYERS and MAX_ENTITIES before processing any .cpp file. Because Scene.h uses #ifndef MAX_LAYERS / #ifndef MAX_ENTITIES, it will not redefine them and your values will be used.

    Effect: - MAX_LAYERS: Number of render layers drawn in Scene::draw() (layer 0 = background, 1+ = sprite context). Increasing this allows more distinct draw layers (e.g. background, platforms, gameplay, foreground, UI). - MAX_ENTITIES: On Arduino, the capacity of the scene entity queue when constructed with this value. On native (mock queue), the value is ignored (unbounded).

    See also: Platforms and Drivers - Scene limits.

    ","text":""},{"location":"api_reference/core/scene/#usage-example","title":"Usage Example","text":"
    #include \"core/Scene.h\"\n#include \"core/Actor.h\"\n\nclass MyGameScene : public pixelroot32::core::Scene {\nprivate:\n    PlayerActor* player;\n\npublic:\n    void init() override {\n        // Create player\n        player = new PlayerActor();\n        player->setPosition(64, 64);\n        addEntity(player);\n\n        // Create some enemies\n        for (int i = 0; i < 5; i++) {\n            EnemyActor* enemy = new EnemyActor();\n            enemy->setPosition(10 + i * 20, 10);\n            addEntity(enemy);\n        }\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Custom game logic\n        if (player->isDead()) {\n            // Handle game over\n        }\n\n        // Update entities and collisions\n        Scene::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw background\n        renderer.drawFilledRectangle(0, 0, 128, 128, Color::Black);\n\n        // Draw all entities\n        Scene::draw(renderer);\n\n        // Draw HUD\n        char scoreText[32];\n        snprintf(scoreText, sizeof(scoreText), \"Score: %d\", score);\n        renderer.drawText(scoreText, 10, 10, Color::White, 1);\n    }\n};\n
    "},{"location":"api_reference/core/scene/#performance-considerations","title":"Performance Considerations","text":"
    • Entity limit: MAX_ENTITIES (default 32) can be overridden via compiler flags; plan accordingly
    • Add/Remove: Frequent add/remove operations can be expensive; use object pooling
    • Update order: Entities are updated in add order; consider order for dependencies
    • Collision checks: CollisionSystem automatically handles actor collisions efficiently
    "},{"location":"api_reference/core/scene/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Each entity consumes memory; stay well below the limit
    • Object pooling: Essential for ESP32 to avoid memory fragmentation
    • Scene switching: Clearing and recreating scenes can fragment memory; reuse scenes when possible
    "},{"location":"api_reference/core/scene/#see-also","title":"See Also","text":"
    • Entity - Base entity class
    • Actor - Entity with collision support
    • PhysicsActor - Entity with physics
    • CollisionSystem - Collision detection
    • Manual - Scenes and Entities
    • API Overview
    "},{"location":"api_reference/graphics/camera2d/","title":"Camera2D","text":"

    2D camera for scrolling and viewport control.

    "},{"location":"api_reference/graphics/camera2d/#description","title":"Description","text":"

    Camera2D controls viewport position and enables scrolling by shifting the renderer's display offset. It supports following targets, boundary constraints, and can be used for parallax effects.

    The camera uses a dead-zone system: it only moves when the target is outside a central zone, creating smooth following behavior.

    "},{"location":"api_reference/graphics/camera2d/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    class Camera2D {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/camera2d/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Scenes (for scrolling and camera control)
    "},{"location":"api_reference/graphics/camera2d/#constructors","title":"Constructors","text":""},{"location":"api_reference/graphics/camera2d/#camera2dint-viewportwidth-int-viewportheight","title":"Camera2D(int viewportWidth, int viewportHeight)","text":"

    Creates a new camera with specified viewport dimensions.

    Parameters: - viewportWidth (int): Width of the viewport in pixels - viewportHeight (int): Height of the viewport in pixels

    Notes: - Viewport size should match display size - Camera position starts at (0, 0) - No boundaries set by default (camera can move anywhere)

    Example:

    #include \"graphics/Camera2D.h\"\n\n// Create camera matching display size\npixelroot32::graphics::Camera2D camera(128, 128);\n\n// Or get from renderer\nint width = renderer.getWidth();\nint height = renderer.getHeight();\npixelroot32::graphics::Camera2D camera(width, height);\n

    "},{"location":"api_reference/graphics/camera2d/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/graphics/camera2d/#void-setpositionfloat-x-float-y","title":"void setPosition(float x, float y)","text":"

    Sets the camera position directly.

    Parameters: - x (float): X position in world space - y (float): Y position in world space

    Returns: - void

    Notes: - Position is clamped to boundaries if set - Use for direct camera control or cutscenes - Overrides any following behavior

    Example:

    camera.setPosition(100.0f, 200.0f);\n

    "},{"location":"api_reference/graphics/camera2d/#void-setboundsfloat-minx-float-maxx","title":"void setBounds(float minX, float maxX)","text":"

    Sets horizontal boundaries for the camera.

    Parameters: - minX (float): Minimum X position - maxX (float): Maximum X position

    Returns: - void

    Notes: - Camera position is clamped to these bounds - Use to prevent camera from going outside level bounds - Set both horizontal and vertical bounds for full constraint

    Example:

    // Level is 512 pixels wide, camera viewport is 128\n// Prevent camera from showing outside level\ncamera.setBounds(0.0f, 512.0f - 128.0f);\n

    "},{"location":"api_reference/graphics/camera2d/#void-setverticalboundsfloat-miny-float-maxy","title":"void setVerticalBounds(float minY, float maxY)","text":"

    Sets vertical boundaries for the camera.

    Parameters: - minY (float): Minimum Y position - maxY (float): Maximum Y position

    Returns: - void

    Notes: - Camera position is clamped to these bounds - Use to prevent camera from going outside level bounds vertically

    Example:

    // Level is 512 pixels tall, camera viewport is 128\ncamera.setVerticalBounds(0.0f, 512.0f - 128.0f);\n

    "},{"location":"api_reference/graphics/camera2d/#void-followtargetfloat-targetx","title":"void followTarget(float targetX)","text":"

    Makes the camera follow a target horizontally only.

    Parameters: - targetX (float): X position of the target to follow

    Returns: - void

    Notes: - Camera follows target with dead-zone behavior - Only horizontal movement; vertical position unchanged - Useful for side-scrolling games

    Example:

    void update(unsigned long deltaTime) override {\n    // Update player position\n    player->update(deltaTime);\n\n    // Camera follows player horizontally\n    camera.followTarget(player->x);\n    camera.apply(renderer);\n}\n

    "},{"location":"api_reference/graphics/camera2d/#void-followtargetfloat-targetx-float-targety","title":"void followTarget(float targetX, float targetY)","text":"

    Makes the camera follow a target in both axes.

    Parameters: - targetX (float): X position of the target to follow - targetY (float): Y position of the target to follow

    Returns: - void

    Notes: - Camera follows target with dead-zone behavior - Both horizontal and vertical following - Useful for top-down or platformer games

    Example:

    void update(unsigned long deltaTime) override {\n    player->update(deltaTime);\n\n    // Camera follows player in both axes\n    camera.followTarget(player->x, player->y);\n    camera.apply(renderer);\n}\n

    "},{"location":"api_reference/graphics/camera2d/#float-getx-const","title":"float getX() const","text":"

    Gets the current X position of the camera.

    Returns: - float: Current X position in world space

    Example:

    float cameraX = camera.getX();\n

    "},{"location":"api_reference/graphics/camera2d/#float-gety-const","title":"float getY() const","text":"

    Gets the current Y position of the camera.

    Returns: - float: Current Y position in world space

    Example:

    float cameraY = camera.getY();\n

    "},{"location":"api_reference/graphics/camera2d/#void-applyrenderer-renderer-const","title":"void apply(Renderer& renderer) const","text":"

    Applies the camera's offset to the renderer.

    Parameters: - renderer (Renderer&): The renderer to apply the camera offset to

    Returns: - void

    Notes: - Sets the renderer's display offset based on camera position - Should be called before drawing world elements - Negative offset is applied (camera moves right = world moves left)

    Example:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Apply camera offset\n    camera.apply(renderer);\n\n    // Draw world (offset applied automatically)\n    renderer.drawTileMap(levelMap, 0, 0, Color::White);\n    renderer.drawSprite(playerSprite, playerX, playerY, Color::White);\n\n    // UI elements (not affected by camera)\n    renderer.setDisplayOffset(0, 0);  // Reset for UI\n    renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n}\n

    "},{"location":"api_reference/graphics/camera2d/#dead-zone-following","title":"Dead-Zone Following","text":"

    The camera uses a dead-zone system for smooth following:

    • Dead zone: Central area where camera doesn't move
    • Following: Camera moves only when target leaves dead zone
    • Smooth: Creates natural, non-jarring camera movement

    Example:

    // Camera follows player with dead zone\nvoid update(unsigned long deltaTime) override {\n    player->update(deltaTime);\n\n    // Camera follows (dead zone handled internally)\n    camera.followTarget(player->x, player->y);\n}\n

    "},{"location":"api_reference/graphics/camera2d/#usage-example","title":"Usage Example","text":"
    #include \"graphics/Camera2D.h\"\n\nclass GameScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    PlayerActor* player;\n    TileMap levelMap;\n\npublic:\n    void init() override {\n        // Create camera matching display size\n        auto& renderer = engine.getRenderer();\n        camera = pixelroot32::graphics::Camera2D(\n            renderer.getWidth(), \n            renderer.getHeight()\n        );\n\n        // Set level boundaries\n        // Level is 512x512, viewport is 128x128\n        camera.setBounds(0.0f, 512.0f - 128.0f);\n        camera.setVerticalBounds(0.0f, 512.0f - 128.0f);\n\n        // Create player\n        player = new PlayerActor(64, 64);\n        addEntity(player);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Camera follows player\n        camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Apply camera\n        camera.apply(renderer);\n\n        // Draw world (camera offset applied)\n        renderer.drawTileMap(levelMap, 0, 0, Color::White);\n\n        // Draw entities (Scene::draw handles this)\n        Scene::draw(renderer);\n\n        // Reset offset for UI\n        renderer.setDisplayOffset(0, 0);\n        renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n    }\n};\n
    "},{"location":"api_reference/graphics/camera2d/#parallax-scrolling","title":"Parallax Scrolling","text":"

    Use multiple cameras or manual offset for parallax:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Background layer (slow parallax)\n    float bgOffsetX = camera.getX() * 0.5f;  // 50% speed\n    renderer.setDisplayOffset(-bgOffsetX, 0);\n    renderer.drawTileMap(backgroundMap, 0, 0, Color::White);\n\n    // Midground layer (normal speed)\n    camera.apply(renderer);\n    renderer.drawTileMap(midgroundMap, 0, 0, Color::White);\n\n    // Foreground (entities, normal speed)\n    Scene::draw(renderer);\n\n    // UI (no offset)\n    renderer.setDisplayOffset(0, 0);\n    renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n}\n
    "},{"location":"api_reference/graphics/camera2d/#performance-considerations","title":"Performance Considerations","text":"
    • Apply frequency: apply() is fast; safe to call every frame
    • Boundary checks: Boundary clamping is efficient
    • Following: Dead-zone calculations are lightweight
    "},{"location":"api_reference/graphics/camera2d/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Float math: Uses floating point; acceptable but integer math would be faster
    • Memory: Camera is small (few floats); minimal memory usage
    "},{"location":"api_reference/graphics/camera2d/#see-also","title":"See Also","text":"
    • Renderer - Rendering system
    • Manual - Cameras and Scrolling
    • API Overview
    "},{"location":"api_reference/graphics/color/","title":"Color","text":"

    Color constants and palette management system.

    "},{"location":"api_reference/graphics/color/#description","title":"Description","text":"

    The Color enum provides color constants that map to palette indices. The engine supports both legacy mode (single global palette) and dual palette mode (separate palettes for backgrounds and sprites).

    Colors are resolved to 16-bit RGB565 values based on the active palette(s).

    "},{"location":"api_reference/graphics/color/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    enum class Color : uint8_t {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/color/#color-enum-values","title":"Color Enum Values","text":""},{"location":"api_reference/graphics/color/#standard-colors-pr32-palette-indices","title":"Standard Colors (PR32 Palette Indices)","text":"
    • Color::Black (0)
    • Color::White (1)
    • Color::Navy (2)
    • Color::Blue (3)
    • Color::Cyan (4)
    • Color::DarkGreen (5)
    • Color::Green (6)
    • Color::LightGreen (7)
    • Color::Yellow (8)
    • Color::Orange (9)
    • Color::LightRed (10)
    • Color::Red (11)
    • Color::DarkRed (12)
    • Color::Purple (13)
    • Color::Magenta (14)
    • Color::Gray (15)
    "},{"location":"api_reference/graphics/color/#color-aliases","title":"Color Aliases","text":"

    For compatibility, several aliases map to the closest available color:

    • Color::DarkBlue \u2192 Navy
    • Color::LightBlue \u2192 Blue
    • Color::Teal \u2192 Cyan
    • Color::Olive \u2192 DarkGreen
    • Color::Gold \u2192 Yellow
    • Color::Brown \u2192 DarkRed
    • Color::Pink \u2192 Magenta
    • Color::LightPurple \u2192 Magenta
    • Color::Maroon \u2192 DarkRed
    • Color::MidGray \u2192 Gray
    • Color::LightGray \u2192 Gray
    • Color::DarkGray \u2192 Gray
    • Color::Silver \u2192 Gray
    "},{"location":"api_reference/graphics/color/#special-colors","title":"Special Colors","text":"
    • Color::Transparent (255): Not a real color; must be handled by renderer (results in no-op)
    • Color::DebugRed \u2192 Red
    • Color::DebugGreen \u2192 Green
    • Color::DebugBlue \u2192 Blue
    "},{"location":"api_reference/graphics/color/#palettetype-enum","title":"PaletteType Enum","text":"

    Built-in palette types:

    • PaletteType::NES: NES color palette
    • PaletteType::GB: Game Boy (4 shades of green)
    • PaletteType::GBC: Game Boy Color palette
    • PaletteType::PICO8: PICO-8 palette
    • PaletteType::PR32: PixelRoot32 default palette
    "},{"location":"api_reference/graphics/color/#palettecontext-enum","title":"PaletteContext Enum","text":"

    Context for palette selection in dual palette mode:

    • PaletteContext::Background: For backgrounds, tilemaps, and background primitives
    • PaletteContext::Sprite: For sprites, characters, and gameplay elements
    "},{"location":"api_reference/graphics/color/#palette-functions","title":"Palette Functions","text":""},{"location":"api_reference/graphics/color/#void-setpalettepalettetype-palette","title":"void setPalette(PaletteType palette)","text":"

    Selects the active color palette (legacy mode). Sets both background and sprite palettes to the same value.

    Parameters: - palette (PaletteType): The palette to use

    Notes: - Does not enable dual palette mode - All rendering uses the same palette - Use for simple games with single palette

    Example:

    pixelroot32::graphics::setPalette(pixelroot32::graphics::PaletteType::NES);\n

    "},{"location":"api_reference/graphics/color/#void-setcustompaletteconst-uint16_t-palette","title":"void setCustomPalette(const uint16_t* palette)","text":"

    Sets a custom color palette (legacy mode). Sets both background and sprite palettes to the same value.

    Parameters: - palette (const uint16_t*): Pointer to an array of 16 uint16_t RGB565 color values

    Notes: - Array must remain valid (use static/global storage) - Engine does not copy the palette - Does not enable dual palette mode

    Example:

    static const uint16_t MY_PALETTE[16] = {\n    0x0000,  // Black\n    0xFFFF,  // White\n    0x001F,  // Blue\n    // ... 13 more colors\n};\n\npixelroot32::graphics::setCustomPalette(MY_PALETTE);\n

    "},{"location":"api_reference/graphics/color/#void-enabledualpalettemodebool-enable","title":"void enableDualPaletteMode(bool enable)","text":"

    Enables or disables dual palette mode.

    Parameters: - enable (bool): true to enable dual palette mode, false for legacy mode

    Notes: - When enabled: backgrounds and sprites use separate palettes - When disabled: single palette for all rendering (legacy mode)

    Example:

    pixelroot32::graphics::enableDualPaletteMode(true);\n

    "},{"location":"api_reference/graphics/color/#void-setbackgroundpalettepalettetype-palette","title":"void setBackgroundPalette(PaletteType palette)","text":"

    Sets the background palette (for backgrounds, tilemaps, etc.).

    Parameters: - palette (PaletteType): The palette type to use for backgrounds

    Notes: - Only used in dual palette mode - Affects tilemaps, background primitives, etc.

    Example:

    pixelroot32::graphics::enableDualPaletteMode(true);\npixelroot32::graphics::setBackgroundPalette(pixelroot32::graphics::PaletteType::NES);\n

    "},{"location":"api_reference/graphics/color/#void-setspritepalettepalettetype-palette","title":"void setSpritePalette(PaletteType palette)","text":"

    Sets the sprite palette (for sprites, characters, etc.).

    Parameters: - palette (PaletteType): The palette type to use for sprites

    Notes: - Only used in dual palette mode - Affects sprites, characters, gameplay elements

    Example:

    pixelroot32::graphics::setSpritePalette(pixelroot32::graphics::PaletteType::GB);\n

    "},{"location":"api_reference/graphics/color/#void-setdualpalettepalettetype-bgpalette-palettetype-spritepalette","title":"void setDualPalette(PaletteType bgPalette, PaletteType spritePalette)","text":"

    Sets both background and sprite palettes at once. Automatically enables dual palette mode.

    Parameters: - bgPalette (PaletteType): The palette type to use for backgrounds - spritePalette (PaletteType): The palette type to use for sprites

    Example:

    pixelroot32::graphics::setDualPalette(\n    pixelroot32::graphics::PaletteType::NES,  // Background\n    pixelroot32::graphics::PaletteType::GB     // Sprites\n);\n

    "},{"location":"api_reference/graphics/color/#void-setbackgroundcustompaletteconst-uint16_t-palette","title":"void setBackgroundCustomPalette(const uint16_t* palette)","text":"

    Sets a custom background palette.

    Parameters: - palette (const uint16_t*): Pointer to an array of 16 uint16_t RGB565 color values

    Notes: - Array must remain valid (use static/global storage) - Only used in dual palette mode

    Example:

    static const uint16_t BG_PALETTE[16] = { /* ... */ };\npixelroot32::graphics::enableDualPaletteMode(true);\npixelroot32::graphics::setBackgroundCustomPalette(BG_PALETTE);\n

    "},{"location":"api_reference/graphics/color/#void-setspritecustompaletteconst-uint16_t-palette","title":"void setSpriteCustomPalette(const uint16_t* palette)","text":"

    Sets a custom sprite palette.

    Parameters: - palette (const uint16_t*): Pointer to an array of 16 uint16_t RGB565 color values

    Notes: - Array must remain valid (use static/global storage) - Only used in dual palette mode

    Example:

    static const uint16_t SPRITE_PALETTE[16] = { /* ... */ };\npixelroot32::graphics::setSpriteCustomPalette(SPRITE_PALETTE);\n

    "},{"location":"api_reference/graphics/color/#void-setdualcustompaletteconst-uint16_t-bgpalette-const-uint16_t-spritepal","title":"void setDualCustomPalette(const uint16_t bgPalette, const uint16_t spritePal)","text":"

    Sets both background and sprite custom palettes at once. Automatically enables dual palette mode.

    Parameters: - bgPalette (const uint16_t): Pointer to background palette array (16 RGB565 values) - spritePal (const uint16_t): Pointer to sprite palette array (16 RGB565 values)

    Example:

    static const uint16_t BG_PAL[16] = { /* ... */ };\nstatic const uint16_t SPRITE_PAL[16] = { /* ... */ };\npixelroot32::graphics::setDualCustomPalette(BG_PAL, SPRITE_PAL);\n

    "},{"location":"api_reference/graphics/color/#uint16_t-resolvecolorcolor-color","title":"uint16_t resolveColor(Color color)","text":"

    Resolves a Color enum to its corresponding 16-bit color value (legacy mode).

    Parameters: - color (Color): The Color enum value

    Returns: - uint16_t: The 16-bit RGB565 color value

    Notes: - Uses the current active palette (single palette mode) - Color::Transparent must not be resolved (handled by renderer) - Typically called internally by renderer

    Example:

    uint16_t rgb565 = pixelroot32::graphics::resolveColor(pixelroot32::graphics::Color::Red);\n

    "},{"location":"api_reference/graphics/color/#uint16_t-resolvecolorcolor-color-palettecontext-context","title":"uint16_t resolveColor(Color color, PaletteContext context)","text":"

    Resolves a Color enum with context (dual palette mode).

    Parameters: - color (Color): The Color enum value - context (PaletteContext): The palette context (Background or Sprite)

    Returns: - uint16_t: The 16-bit RGB565 color value

    Notes: - Uses the appropriate palette based on context - In legacy mode, context is ignored - Color::Transparent must not be resolved

    Example:

    uint16_t bgColor = pixelroot32::graphics::resolveColor(\n    pixelroot32::graphics::Color::Blue,\n    pixelroot32::graphics::PaletteContext::Background\n);\n

    "},{"location":"api_reference/graphics/color/#rgb565-format","title":"RGB565 Format","text":"

    Colors are stored as 16-bit RGB565 values:

    • Bits 15-11: Red (5 bits, 0-31)
    • Bits 10-5: Green (6 bits, 0-63)
    • Bits 4-0: Blue (5 bits, 0-31)

    Conversion Example:

    // Convert RGB to RGB565\nuint16_t rgb565 = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);\n

    "},{"location":"api_reference/graphics/color/#usage-examples","title":"Usage Examples","text":""},{"location":"api_reference/graphics/color/#legacy-mode-single-palette","title":"Legacy Mode (Single Palette)","text":"
    // Set single palette for all rendering\npixelroot32::graphics::setPalette(pixelroot32::graphics::PaletteType::NES);\n\n// Use colors\nrenderer.drawSprite(sprite, 100, 100, pixelroot32::graphics::Color::Red);\nrenderer.drawFilledRectangle(10, 10, 50, 50, pixelroot32::graphics::Color::Blue);\n
    "},{"location":"api_reference/graphics/color/#dual-palette-mode","title":"Dual Palette Mode","text":"
    // Enable dual palette mode\npixelroot32::graphics::enableDualPaletteMode(true);\n\n// Set different palettes for backgrounds and sprites\npixelroot32::graphics::setBackgroundPalette(pixelroot32::graphics::PaletteType::NES);\npixelroot32::graphics::setSpritePalette(pixelroot32::graphics::PaletteType::GB);\n\n// Or use convenience function\npixelroot32::graphics::setDualPalette(\n    pixelroot32::graphics::PaletteType::NES,\n    pixelroot32::graphics::PaletteType::GB\n);\n
    "},{"location":"api_reference/graphics/color/#custom-palettes","title":"Custom Palettes","text":"
    // Define custom palette (RGB565 values)\nstatic const uint16_t CUSTOM_PALETTE[16] = {\n    0x0000,  // 0: Black\n    0xFFFF,  // 1: White\n    0x001F,  // 2: Blue\n    0x07E0,  // 3: Green\n    0xF800,  // 4: Red\n    // ... 11 more colors\n};\n\n// Use in legacy mode\npixelroot32::graphics::setCustomPalette(CUSTOM_PALETTE);\n\n// Or use in dual palette mode\npixelroot32::graphics::enableDualPaletteMode(true);\npixelroot32::graphics::setBackgroundCustomPalette(CUSTOM_PALETTE);\npixelroot32::graphics::setSpriteCustomPalette(CUSTOM_PALETTE);\n
    "},{"location":"api_reference/graphics/color/#performance-considerations","title":"Performance Considerations","text":"
    • Color resolution: Fast lookup operation
    • Palette switching: Changing palettes is fast (just pointer assignment)
    • Memory: Palettes are stored in flash (const arrays) for best performance
    • Dual mode: Slightly more overhead than legacy mode, but minimal
    "},{"location":"api_reference/graphics/color/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Flash storage: Store custom palettes in flash (const/constexpr)
    • Memory: Palettes are small (16 uint16_t = 32 bytes)
    • Palette switching: Avoid switching palettes every frame
    "},{"location":"api_reference/graphics/color/#see-also","title":"See Also","text":"
    • Renderer - Rendering system
    • Manual - Color Palettes
    • API Overview
    "},{"location":"api_reference/graphics/display_config/","title":"DisplayConfig","text":"

    Configuration settings for initializing the display.

    "},{"location":"api_reference/graphics/display_config/#description","title":"Description","text":"

    DisplayConfig holds display parameters used by the renderer and camera to draw correctly on the target device. It defines the display type, dimensions, rotation, and creates the appropriate DrawSurface implementation for the platform.

    "},{"location":"api_reference/graphics/display_config/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    struct DisplayConfig {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/display_config/#displaytype-enum","title":"DisplayType Enum","text":"

    Supported display types:

    • DisplayType::ST7789: 240x240 TFT display
    • DisplayType::ST7735: 128x128 TFT display
    • DisplayType::NONE: For SDL2 native (no driver needed)
    "},{"location":"api_reference/graphics/display_config/#structure","title":"Structure","text":""},{"location":"api_reference/graphics/display_config/#displaytype-type","title":"DisplayType type","text":"

    The type of display.

    Type: DisplayType enum

    Access: Read-write

    Notes: - Determines which driver to use (ESP32) - NONE for Native/SDL2 platform

    "},{"location":"api_reference/graphics/display_config/#int-rotation","title":"int rotation","text":"

    Display rotation in degrees.

    Type: int

    Access: Read-write

    Default: 0

    Notes: - Common values: 0, 90, 180, 270 - Rotation is applied during initialization - Some displays may not support all rotations

    "},{"location":"api_reference/graphics/display_config/#uint16_t-width","title":"uint16_t width","text":"

    Display width in pixels.

    Type: uint16_t

    Access: Read-write

    Default: 240

    Notes: - Should match actual display width - Used for viewport and camera calculations

    "},{"location":"api_reference/graphics/display_config/#uint16_t-height","title":"uint16_t height","text":"

    Display height in pixels.

    Type: uint16_t

    Access: Read-write

    Default: 240

    Notes: - Should match actual display height - Used for viewport and camera calculations

    "},{"location":"api_reference/graphics/display_config/#int-xoffset","title":"int xOffset","text":"

    X offset for display alignment.

    Type: int

    Access: Read-write

    Default: 0

    Notes: - Used to adjust display position - Some displays need offset for proper alignment

    "},{"location":"api_reference/graphics/display_config/#int-yoffset","title":"int yOffset","text":"

    Y offset for display alignment.

    Type: int

    Access: Read-write

    Default: 0

    Notes: - Used to adjust display position - Some displays need offset for proper alignment

    "},{"location":"api_reference/graphics/display_config/#drawsurface-getdrawsurface-const","title":"DrawSurface& getDrawSurface() const","text":"

    Gets the underlying DrawSurface implementation.

    Returns: - DrawSurface&: Reference to the DrawSurface

    Notes: - Advanced usage: typically not needed - Provides access to low-level display driver - Platform-specific implementation

    "},{"location":"api_reference/graphics/display_config/#constructors","title":"Constructors","text":""},{"location":"api_reference/graphics/display_config/#displayconfigdisplaytype-type-const-int-rot-0-uint16_t-w-240-uint16_t-h-240-const-int-xoffset-0-const-int-yoffset-0","title":"DisplayConfig(DisplayType type, const int rot = 0, uint16_t w = 240, uint16_t h = 240, const int xOffset = 0, const int yOffset = 0)","text":"

    Constructs a DisplayConfig with specified parameters.

    Parameters: - type (DisplayType): The display type - rot (int, optional): Rotation in degrees. Default: 0 - w (uint16_t, optional): Width in pixels. Default: 240 - h (uint16_t, optional): Height in pixels. Default: 240 - xOffset (int, optional): X offset. Default: 0 - yOffset (int, optional): Y offset. Default: 0

    Notes: - Automatically creates the appropriate DrawSurface for the platform - ESP32: Creates TFT_eSPI_Drawer based on display type - Native: Creates SDL2_Drawer

    Example:

    // ST7789 display, 240x240, no rotation\npixelroot32::graphics::DisplayConfig config(\n    pixelroot32::graphics::DisplayType::ST7789\n);\n\n// ST7735 display, 128x128, rotated 90 degrees\npixelroot32::graphics::DisplayConfig config(\n    pixelroot32::graphics::DisplayType::ST7735,\n    90,   // rotation\n    128,  // width\n    128   // height\n);\n\n// Native/SDL2 (no specific display type)\npixelroot32::graphics::DisplayConfig config(\n    pixelroot32::graphics::DisplayType::NONE,\n    0,    // rotation\n    128,  // width\n    128   // height\n);\n

    "},{"location":"api_reference/graphics/display_config/#usage-examples","title":"Usage Examples","text":""},{"location":"api_reference/graphics/display_config/#esp32-with-st7789","title":"ESP32 with ST7789","text":"
    #ifdef PLATFORM_ESP32\n#include \"graphics/DisplayConfig.h\"\n\nvoid setup() {\n    // Configure ST7789 display (240x240)\n    pixelroot32::graphics::DisplayConfig displayConfig(\n        pixelroot32::graphics::DisplayType::ST7789,\n        0,    // rotation\n        240,  // width\n        240   // height\n    );\n\n    // Use with Engine\n    pixelroot32::core::Engine engine(displayConfig);\n    engine.init();\n    engine.run();\n}\n#endif\n
    "},{"location":"api_reference/graphics/display_config/#esp32-with-st7735","title":"ESP32 with ST7735","text":"
    #ifdef PLATFORM_ESP32\n    // Configure ST7735 display (128x128)\n    pixelroot32::graphics::DisplayConfig displayConfig(\n        pixelroot32::graphics::DisplayType::ST7735,\n        0,    // rotation\n        128,  // width\n        128   // height\n    );\n#endif\n
    "},{"location":"api_reference/graphics/display_config/#nativesdl2","title":"Native/SDL2","text":"
    #ifdef PLATFORM_NATIVE\n    // Native display (SDL2 window)\n    pixelroot32::graphics::DisplayConfig displayConfig(\n        pixelroot32::graphics::DisplayType::NONE,\n        0,    // rotation\n        128,  // width\n        128   // height\n    );\n#endif\n
    "},{"location":"api_reference/graphics/display_config/#platform-agnostic-setup","title":"Platform-Agnostic Setup","text":"
    #include \"graphics/DisplayConfig.h\"\n#include \"core/Engine.h\"\n\nvoid setup() {\n    pixelroot32::graphics::DisplayConfig displayConfig;\n\n    #ifdef PLATFORM_ESP32\n        displayConfig.type = pixelroot32::graphics::DisplayType::ST7789;\n        displayConfig.width = 240;\n        displayConfig.height = 240;\n    #elif PLATFORM_NATIVE\n        displayConfig.type = pixelroot32::graphics::DisplayType::NONE;\n        displayConfig.width = 128;\n        displayConfig.height = 128;\n    #endif\n\n    displayConfig.rotation = 0;\n\n    pixelroot32::core::Engine engine(displayConfig);\n    engine.init();\n    engine.run();\n}\n
    "},{"location":"api_reference/graphics/display_config/#display-type-details","title":"Display Type Details","text":""},{"location":"api_reference/graphics/display_config/#st7789","title":"ST7789","text":"
    • Resolution: Typically 240x240 or 240x320
    • Interface: SPI
    • Driver: TFT_eSPI
    • Common sizes: 240x240, 240x320
    "},{"location":"api_reference/graphics/display_config/#st7735","title":"ST7735","text":"
    • Resolution: Typically 128x128 or 128x160
    • Interface: SPI
    • Driver: TFT_eSPI
    • Common sizes: 128x128, 128x160
    "},{"location":"api_reference/graphics/display_config/#none-native","title":"NONE (Native)","text":"
    • Platform: Native/SDL2
    • Driver: SDL2_Drawer
    • Resolution: Configurable (any size)
    • Window: Creates SDL2 window
    "},{"location":"api_reference/graphics/display_config/#rotation","title":"Rotation","text":"

    Display rotation values:

    • 0: Normal orientation
    • 90: Rotated 90 degrees clockwise
    • 180: Rotated 180 degrees
    • 270: Rotated 90 degrees counter-clockwise

    Notes: - Rotation affects coordinate system - Some displays may not support all rotations - Test rotation on your specific hardware

    "},{"location":"api_reference/graphics/display_config/#performance-considerations","title":"Performance Considerations","text":"
    • Initialization: Display initialization happens once at startup
    • Driver selection: Automatic based on platform and type
    • Memory: DisplayConfig is small (few fields)
    "},{"location":"api_reference/graphics/display_config/#esp32-considerations","title":"ESP32 Considerations","text":""},{"location":"api_reference/graphics/display_config/#tft_espi-configuration","title":"TFT_eSPI Configuration","text":"

    DisplayConfig uses TFT_eSPI driver. Additional configuration may be needed in platformio.ini:

    build_flags =\n    -DUSER_SETUP_LOADED=1\n    -DST7789_DRIVER=1\n    -DTFT_WIDTH=240\n    -DTFT_HEIGHT=240\n    # ... pin configuration\n
    "},{"location":"api_reference/graphics/display_config/#pin-configuration","title":"Pin Configuration","text":"

    GPIO pins must be configured separately (not in DisplayConfig):

    • MOSI: Data pin
    • SCLK: Clock pin
    • DC: Data/Command pin
    • RST: Reset pin
    • CS: Chip select pin (optional)
    "},{"location":"api_reference/graphics/display_config/#see-also","title":"See Also","text":"
    • Renderer - Rendering system
    • Camera2D - Camera that uses display dimensions
    • Manual - Platforms and Drivers
    • API Overview
    "},{"location":"api_reference/graphics/font/","title":"Font","text":"

    Descriptor for a bitmap font using 1bpp sprites.

    "},{"location":"api_reference/graphics/font/#description","title":"Description","text":"

    A Font contains an array of Sprite structures, one for each character in the font's character set. Each glyph is rendered as a 1bpp sprite, allowing consistent rendering across platforms.

    The font uses fixed-width glyphs for simplicity and performance. All glyphs share the same width and height, with spacing between characters controlled by the spacing field.

    "},{"location":"api_reference/graphics/font/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    struct Font {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/font/#structure","title":"Structure","text":""},{"location":"api_reference/graphics/font/#const-sprite-glyphs","title":"const Sprite* glyphs","text":"

    Array of sprites, one per character (indexed by character code - firstChar).

    Type: const Sprite*

    Access: Read-only

    Notes: - Array must contain sprites for all characters from firstChar to lastChar - Each sprite represents one character glyph - Should be stored in flash (const/constexpr) for best performance

    Example:

    static const Sprite FONT_GLYPHS[] = {\n    spaceGlyph,    // Index 0 = ' ' (firstChar = 32)\n    exclamationGlyph, // Index 1 = '!'\n    // ... more glyphs\n};\n\nstatic const Font myFont = {\n    FONT_GLYPHS,\n    32,  // firstChar\n    126, // lastChar\n    5,   // glyphWidth\n    7,   // glyphHeight\n    1,   // spacing\n    8    // lineHeight\n};\n

    "},{"location":"api_reference/graphics/font/#uint8_t-firstchar","title":"uint8_t firstChar","text":"

    First character code in the font.

    Type: uint8_t

    Access: Read-only

    Default: Typically 32 (space character)

    Notes: - ASCII code of the first character - Common: 32 (space ' ') for ASCII fonts - Glyphs array starts at index 0 for this character

    Example:

    firstChar = 32;  // Starts at space character\n

    "},{"location":"api_reference/graphics/font/#uint8_t-lastchar","title":"uint8_t lastChar","text":"

    Last character code in the font.

    Type: uint8_t

    Access: Read-only

    Default: Typically 126 (tilde '~')

    Notes: - ASCII code of the last character - Common: 126 (tilde '~') for full ASCII fonts - Glyphs array must contain (lastChar - firstChar + 1) sprites

    Example:

    lastChar = 126;  // Ends at tilde character\n// Font contains characters 32-126 (95 characters)\n

    "},{"location":"api_reference/graphics/font/#uint8_t-glyphwidth","title":"uint8_t glyphWidth","text":"

    Fixed width of each glyph in pixels.

    Type: uint8_t

    Access: Read-only

    Notes: - All glyphs must have the same width - Typical values: 5, 6, 8 pixels - Smaller = more characters per line, less readable

    Example:

    glyphWidth = 5;  // 5 pixels wide (like FONT_5X7)\n

    "},{"location":"api_reference/graphics/font/#uint8_t-glyphheight","title":"uint8_t glyphHeight","text":"

    Fixed height of each glyph in pixels.

    Type: uint8_t

    Access: Read-only

    Notes: - All glyphs must have the same height - Typical values: 7, 8, 10 pixels - Smaller = more lines, less readable

    Example:

    glyphHeight = 7;  // 7 pixels tall (like FONT_5X7)\n

    "},{"location":"api_reference/graphics/font/#uint8_t-spacing","title":"uint8_t spacing","text":"

    Horizontal spacing between characters in pixels.

    Type: uint8_t

    Access: Read-only

    Default: Typically 1

    Notes: - Space added between characters - 0 = no spacing (characters touch) - 1 = 1 pixel gap (common) - Higher values = more readable but wider text

    Example:

    spacing = 1;  // 1 pixel between characters\n

    "},{"location":"api_reference/graphics/font/#uint8_t-lineheight","title":"uint8_t lineHeight","text":"

    Total line height including vertical spacing.

    Type: uint8_t

    Access: Read-only

    Notes: - Should be glyphHeight + verticalSpacing - Used for line breaks and multi-line text - Typical: glyphHeight + 1 or glyphHeight + 2

    Example:

    lineHeight = 8;  // 7 pixel glyph + 1 pixel spacing\n

    "},{"location":"api_reference/graphics/font/#built-in-fonts","title":"Built-in Fonts","text":""},{"location":"api_reference/graphics/font/#font_5x7","title":"FONT_5X7","text":"

    Standard 5x7 pixel font (built-in).

    Properties: - Width: 5 pixels - Height: 7 pixels - Characters: Typically ASCII 32-126 - Spacing: 1 pixel - Line height: 8 pixels

    Usage:

    #include \"graphics/Font.h\"\n\nrenderer.drawText(\"Hello\", 10, 10, Color::White, 1, &FONT_5X7);\n

    "},{"location":"api_reference/graphics/font/#creating-custom-fonts","title":"Creating Custom Fonts","text":""},{"location":"api_reference/graphics/font/#step-1-create-glyph-sprites","title":"Step 1: Create Glyph Sprites","text":"
    // Space character (ASCII 32)\nstatic const uint16_t SPACE_GLYPH[] = {\n    0b00000,\n    0b00000,\n    0b00000,\n    0b00000,\n    0b00000,\n    0b00000,\n    0b00000\n};\n\n// 'A' character (ASCII 65)\nstatic const uint16_t A_GLYPH[] = {\n    0b00100,\n    0b01010,\n    0b10001,\n    0b11111,\n    0b10001,\n    0b10001,\n    0b00000\n};\n\n// ... more glyphs\n
    "},{"location":"api_reference/graphics/font/#step-2-create-glyph-array","title":"Step 2: Create Glyph Array","text":"
    static const Sprite FONT_GLYPHS[] = {\n    {SPACE_GLYPH, 5, 7},  // Index 0 = ' ' (32)\n    // ... more glyphs\n    {A_GLYPH, 5, 7},      // Index 33 = 'A' (65)\n    // ... more glyphs\n};\n
    "},{"location":"api_reference/graphics/font/#step-3-create-font-structure","title":"Step 3: Create Font Structure","text":"
    static const Font MY_FONT = {\n    FONT_GLYPHS,  // glyphs array\n    32,           // firstChar (space)\n    126,          // lastChar (tilde)\n    5,            // glyphWidth\n    7,            // glyphHeight\n    1,            // spacing\n    8             // lineHeight\n};\n
    "},{"location":"api_reference/graphics/font/#step-4-use-font","title":"Step 4: Use Font","text":"
    renderer.drawText(\"Hello\", 10, 10, Color::White, 1, &MY_FONT);\n
    "},{"location":"api_reference/graphics/font/#usage-example","title":"Usage Example","text":"
    #include \"graphics/Font.h\"\n#include \"graphics/Renderer.h\"\n\n// Using built-in font\nvoid draw(Renderer& renderer) override {\n    // Default font\n    renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n\n    // Explicit font\n    renderer.drawText(\"Score: 100\", 10, 30, Color::White, 1, &FONT_5X7);\n\n    // Centered text with font\n    renderer.drawTextCentered(\"Game Over\", 64, Color::Yellow, 2, &FONT_5X7);\n}\n\n// Custom font example\nstatic const uint16_t CUSTOM_GLYPHS[][7] = {\n    // Space, A, B, C, etc.\n};\n\nstatic const Sprite CUSTOM_SPRITES[] = {\n    {CUSTOM_GLYPHS[0], 6, 8},  // Space\n    {CUSTOM_GLYPHS[1], 6, 8},  // A\n    // ... more\n};\n\nstatic const Font CUSTOM_FONT = {\n    CUSTOM_SPRITES,\n    32,   // firstChar\n    90,   // lastChar (A-Z only)\n    6,    // width\n    8,    // height\n    1,    // spacing\n    9     // lineHeight\n};\n\nvoid draw(Renderer& renderer) override {\n    renderer.drawText(\"CUSTOM\", 10, 10, Color::White, 1, &CUSTOM_FONT);\n}\n
    "},{"location":"api_reference/graphics/font/#text-sizing","title":"Text Sizing","text":"

    Calculate text dimensions:

    int getTextWidth(const Font* font, const char* text) {\n    if (!font || !text) return 0;\n\n    int width = 0;\n    for (int i = 0; text[i] != '\\0'; i++) {\n        if (text[i] >= font->firstChar && text[i] <= font->lastChar) {\n            width += font->glyphWidth + font->spacing;\n        }\n    }\n    return width - font->spacing;  // Remove last spacing\n}\n\nint getTextHeight(const Font* font) {\n    return font ? font->lineHeight : 8;\n}\n
    "},{"location":"api_reference/graphics/font/#performance-considerations","title":"Performance Considerations","text":"
    • Font storage: Store fonts in flash (const/constexpr) for best performance
    • Glyph lookup: Fast array access (character code - firstChar)
    • Fixed width: Fixed-width fonts are faster than variable-width
    • Font switching: Changing fonts is fast (just pointer assignment)
    "},{"location":"api_reference/graphics/font/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Store font data in flash, not RAM
    • Font size: Larger fonts use more flash memory
    • Character range: Limit character range to save memory if not needed
    "},{"location":"api_reference/graphics/font/#see-also","title":"See Also","text":"
    • Renderer - Rendering system that uses fonts
    • Sprite - Sprite structure used for glyphs
    • Manual - Basic Rendering
    • API Overview
    "},{"location":"api_reference/graphics/renderer/","title":"Renderer","text":"

    High-level graphics rendering system for drawing shapes, text, sprites, and tilemaps.

    "},{"location":"api_reference/graphics/renderer/#description","title":"Description","text":"

    The Renderer class provides a unified API for drawing shapes, text, and images. It abstracts the underlying hardware implementation (DrawSurface) and manages display configuration, including rotation and offsets.

    The renderer uses integer-only math for optimal performance on ESP32 and supports multiple sprite formats (1bpp, 2bpp, 4bpp) and multi-layer sprites.

    "},{"location":"api_reference/graphics/renderer/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    class Renderer {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/renderer/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Engine (manages renderer instance)
    "},{"location":"api_reference/graphics/renderer/#constructors","title":"Constructors","text":""},{"location":"api_reference/graphics/renderer/#rendererconst-displayconfig-config","title":"Renderer(const DisplayConfig& config)","text":"

    Constructs the Renderer with a specific display configuration.

    Parameters: - config (const DisplayConfig&): The display configuration settings (width, height, rotation, etc.)

    Example:

    #include \"graphics/Renderer.h\"\n#include \"graphics/DisplayConfig.h\"\n\npixelroot32::graphics::DisplayConfig config;\nconfig.width = 128;\nconfig.height = 128;\nconfig.rotation = 0;\n\npixelroot32::graphics::Renderer renderer(config);\nrenderer.init();\n

    "},{"location":"api_reference/graphics/renderer/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/graphics/renderer/#void-init","title":"void init()","text":"

    Initializes the renderer and the underlying draw surface.

    Returns: - void

    Notes: - Must be called after construction and before any drawing operations - Initializes the platform-specific DrawSurface implementation - Safe to call multiple times (idempotent)

    Example:

    Renderer renderer(displayConfig);\nrenderer.init();  // Initialize before use\n

    "},{"location":"api_reference/graphics/renderer/#void-beginframe","title":"void beginFrame()","text":"

    Prepares the buffer for a new frame (clears screen).

    Returns: - void

    Notes: - Should be called once at the start of each frame - Clears the display buffer - Typically called automatically by Engine, but can be called manually

    Example:

    void draw(Renderer& renderer) override {\n    renderer.beginFrame();\n    // Draw everything...\n    renderer.endFrame();\n}\n

    "},{"location":"api_reference/graphics/renderer/#void-endframe","title":"void endFrame()","text":"

    Finalizes the frame and sends the buffer to the display.

    Returns: - void

    Notes: - Should be called once at the end of each frame - Sends the completed frame buffer to the display - Typically called automatically by Engine, but can be called manually

    "},{"location":"api_reference/graphics/renderer/#drawsurface-getdrawsurface","title":"DrawSurface& getDrawSurface()","text":"

    Gets the underlying DrawSurface implementation.

    Returns: - DrawSurface&: Reference to the DrawSurface

    Notes: - Advanced usage: typically not needed unless implementing custom drawing - Provides low-level access to the display driver

    "},{"location":"api_reference/graphics/renderer/#void-drawtextconst-char-text-int16_t-x-int16_t-y-color-color-uint8_t-size","title":"void drawText(const char* text, int16_t x, int16_t y, Color color, uint8_t size)","text":"

    Draws a string of text using the default font.

    Parameters: - text (const char*): The text to draw (null-terminated string) - x (int16_t): X coordinate (top-left corner of text) - y (int16_t): Y coordinate (top-left corner of text) - color (Color): Text color - size (uint8_t): Text size multiplier (1 = normal, 2 = double, etc.)

    Performance Notes: - Efficient for small amounts of text - Avoid calling in tight loops with long strings - Use static buffers for text formatting

    Example:

    renderer.drawText(\"Hello World\", 10, 10, Color::White, 1);\nrenderer.drawText(\"Score: 100\", 10, 30, Color::Yellow, 2);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawtextconst-char-text-int16_t-x-int16_t-y-color-color-uint8_t-size-const-font-font","title":"void drawText(const char text, int16_t x, int16_t y, Color color, uint8_t size, const Font font)","text":"

    Draws a string of text using a specific font.

    Parameters: - text (const char): The text to draw - x (int16_t): X coordinate - y (int16_t): Y coordinate - color (Color): Text color - size (uint8_t): Text size multiplier - font (const Font): Pointer to the font to use. If nullptr, uses the default font

    Example:

    const Font* customFont = &FONT_5X7;\nrenderer.drawText(\"Custom Font\", 10, 10, Color::White, 1, customFont);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawtextcenteredconst-char-text-int16_t-y-color-color-uint8_t-size","title":"void drawTextCentered(const char* text, int16_t y, Color color, uint8_t size)","text":"

    Draws text centered horizontally at a given Y coordinate using the default font.

    Parameters: - text (const char*): The text to draw - y (int16_t): Y coordinate - color (Color): Text color - size (uint8_t): Text size

    Example:

    renderer.drawTextCentered(\"Game Over\", 64, Color::White, 2);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawtextcenteredconst-char-text-int16_t-y-color-color-uint8_t-size-const-font-font","title":"void drawTextCentered(const char text, int16_t y, Color color, uint8_t size, const Font font)","text":"

    Draws text centered horizontally at a given Y coordinate using a specific font.

    Parameters: - text (const char): The text to draw - y (int16_t): Y coordinate - color (Color): Text color - size (uint8_t): Text size - font (const Font): Pointer to the font to use. If nullptr, uses the default font

    "},{"location":"api_reference/graphics/renderer/#void-drawfilledcircleint-x-int-y-int-radius-color-color","title":"void drawFilledCircle(int x, int y, int radius, Color color)","text":"

    Draws a filled circle.

    Parameters: - x (int): Center X coordinate - y (int): Center Y coordinate - radius (int): Radius of the circle in pixels - color (Color): Fill color

    Example:

    renderer.drawFilledCircle(64, 64, 20, Color::Red);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawcircleint-x-int-y-int-radius-color-color","title":"void drawCircle(int x, int y, int radius, Color color)","text":"

    Draws a circle outline.

    Parameters: - x (int): Center X coordinate - y (int): Center Y coordinate - radius (int): Radius of the circle in pixels - color (Color): Outline color

    Example:

    renderer.drawCircle(64, 64, 20, Color::White);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawrectangleint-x-int-y-int-width-int-height-color-color","title":"void drawRectangle(int x, int y, int width, int height, Color color)","text":"

    Draws a rectangle outline.

    Parameters: - x (int): Top-left X coordinate - y (int): Top-left Y coordinate - width (int): Width of the rectangle in pixels - height (int): Height of the rectangle in pixels - color (Color): Outline color

    Example:

    renderer.drawRectangle(10, 10, 100, 50, Color::Blue);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawfilledrectangleint-x-int-y-int-width-int-height-color-color","title":"void drawFilledRectangle(int x, int y, int width, int height, Color color)","text":"

    Draws a filled rectangle.

    Parameters: - x (int): Top-left X coordinate - y (int): Top-left Y coordinate - width (int): Width of the rectangle in pixels - height (int): Height of the rectangle in pixels - color (Color): Fill color

    Example:

    renderer.drawFilledRectangle(10, 10, 100, 50, Color::Green);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawlineint-x1-int-y1-int-x2-int-y2-color-color","title":"void drawLine(int x1, int y1, int x2, int y2, Color color)","text":"

    Draws a line between two points.

    Parameters: - x1 (int): Start X coordinate - y1 (int): Start Y coordinate - x2 (int): End X coordinate - y2 (int): End Y coordinate - color (Color): Line color

    Example:

    renderer.drawLine(0, 0, 128, 128, Color::White);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawpixelint-x-int-y-color-color","title":"void drawPixel(int x, int y, Color color)","text":"

    Draws a single pixel.

    Parameters: - x (int): X coordinate - y (int): Y coordinate - color (Color): Pixel color

    Performance Notes: - Very fast, but avoid calling thousands of times per frame - Use for special effects or debugging

    Example:

    renderer.drawPixel(64, 64, Color::Red);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawspriteconst-sprite-sprite-int-x-int-y-color-color-bool-flipx-false","title":"void drawSprite(const Sprite& sprite, int x, int y, Color color, bool flipX = false)","text":"

    Draws a 1bpp monochrome sprite using the Sprite descriptor.

    Parameters: - sprite (const Sprite&): Sprite descriptor (data, width, height) - x (int): Top-left X coordinate in logical screen space - y (int): Top-left Y coordinate in logical screen space - color (Color): Color used for \"on\" pixels. Default: uses sprite palette context - flipX (bool, optional): If true, sprite is mirrored horizontally. Default: false

    Performance Notes: - Very efficient for 1bpp sprites (integer-only operations) - Sprite data should be stored in flash (const/constexpr) for best performance - Avoid calling in tight loops; batch similar operations when possible

    Example:

    static const uint16_t playerData[] = {\n    0b00111100,\n    0b01111110,\n    // ... more rows\n};\n\nstatic const Sprite playerSprite = {\n    playerData,\n    8,  // width\n    8   // height\n};\n\nrenderer.drawSprite(playerSprite, 100, 100, Color::White);\nrenderer.drawSprite(playerSprite, 120, 100, Color::White, true);  // Flipped\n

    "},{"location":"api_reference/graphics/renderer/#void-drawspriteconst-sprite-sprite-int-x-int-y-float-scalex-float-scaley-color-color-bool-flipx-false","title":"void drawSprite(const Sprite& sprite, int x, int y, float scaleX, float scaleY, Color color, bool flipX = false)","text":"

    Draws a scaled 1bpp monochrome sprite.

    Parameters: - sprite (const Sprite&): Sprite descriptor - x (int): Top-left X coordinate - y (int): Top-left Y coordinate - scaleX (float): Horizontal scaling factor (e.g., 1.25 for 25% larger) - scaleY (float): Vertical scaling factor - color (Color): Color used for \"on\" pixels - flipX (bool, optional): If true, sprite is mirrored horizontally before scaling. Default: false

    Performance Notes: - Slower than non-scaled version due to scaling calculations - Use integer scaling factors when possible (1.0, 2.0, etc.) for better performance

    Example:

    renderer.drawSprite(playerSprite, 100, 100, 2.0f, 2.0f, Color::White);  // 2x size\n

    "},{"location":"api_reference/graphics/renderer/#void-drawmultispriteconst-multisprite-sprite-int-x-int-y","title":"void drawMultiSprite(const MultiSprite& sprite, int x, int y)","text":"

    Draws a multi-layer sprite composed of several 1bpp layers.

    Parameters: - sprite (const MultiSprite&): Multi-layer sprite descriptor - x (int): Top-left X coordinate in logical screen space - y (int): Top-left Y coordinate in logical screen space

    Performance Notes: - Each layer is rendered separately, so more layers = more draw calls - Still efficient as each layer uses 1bpp format - Use for multi-color sprites without higher bit-depths

    Example:

    static const SpriteLayer layers[] = {\n    { outlineData, Color::Black },\n    { fillData, Color::Red },\n    { highlightData, Color::Yellow }\n};\n\nstatic const MultiSprite playerMultiSprite = {\n    8,      // width\n    8,      // height\n    layers, // layers array\n    3       // layer count\n};\n\nrenderer.drawMultiSprite(playerMultiSprite, 100, 100);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawmultispriteconst-multisprite-sprite-int-x-int-y-float-scalex-float-scaley","title":"void drawMultiSprite(const MultiSprite& sprite, int x, int y, float scaleX, float scaleY)","text":"

    Draws a scaled multi-layer sprite.

    Parameters: - sprite (const MultiSprite&): Multi-layer sprite descriptor - x (int): Top-left X coordinate - y (int): Top-left Y coordinate - scaleX (float): Horizontal scaling factor - scaleY (float): Vertical scaling factor

    "},{"location":"api_reference/graphics/renderer/#void-drawtilemapconst-tilemap-map-int-originx-int-originy-color-color","title":"void drawTileMap(const TileMap& map, int originX, int originY, Color color)","text":"

    Draws a 1bpp tilemap.

    Parameters: - map (const TileMap&): Tilemap descriptor (indices, 1bpp tiles, dimensions) - originX (int): X coordinate of the top-left corner - originY (int): Y coordinate of the top-left corner - color (Color): Color used for all tiles in the map

    "},{"location":"api_reference/graphics/renderer/#void-drawtilemapconst-tilemap2bpp-map-int-originx-int-originy","title":"void drawTileMap(const TileMap2bpp& map, int originX, int originY)","text":"

    Draws a 2bpp tilemap. Available when PIXELROOT32_ENABLE_2BPP_SPRITES is defined.

    Parameters: - map (const TileMap2bpp&): Tilemap descriptor (indices, 2bpp tiles, dimensions) - originX (int): X coordinate - originY (int): Y coordinate

    "},{"location":"api_reference/graphics/renderer/#void-drawtilemapconst-tilemap4bpp-map-int-originx-int-originy","title":"void drawTileMap(const TileMap4bpp& map, int originX, int originY)","text":"

    Draws a 4bpp tilemap. Available when PIXELROOT32_ENABLE_4BPP_SPRITES is defined.

    Parameters: - map (const TileMap4bpp&): Tilemap descriptor (indices, 4bpp tiles, dimensions) - originX (int): X coordinate - originY (int): Y coordinate

    Performance Notes: - Very efficient for rendering large backgrounds - Only visible tiles are drawn (viewport culling) - Use tilemaps instead of individual sprites for backgrounds

    Example:

    static const uint8_t levelIndices[] = {\n    0, 1, 2, 3,\n    4, 5, 6, 7,\n    // ... more rows\n};\n\nstatic const TileMap levelMap = {\n    levelIndices,\n    16,        // width in tiles\n    16,        // height in tiles\n    tileSprites, // tile sprite array\n    8,         // tile width\n    8,         // tile height\n    16         // tile count\n};\n\nrenderer.drawTileMap(levelMap, 0, 0, Color::White);\n

    "},{"location":"api_reference/graphics/renderer/#void-setdisplayoffsetint-x-int-y","title":"void setDisplayOffset(int x, int y)","text":"

    Sets a global offset for all drawing operations. Useful for camera/parallax effects.

    Parameters: - x (int): X offset in pixels - y (int): Y offset in pixels

    Notes: - All subsequent drawing operations are offset by this amount - Useful for camera scrolling and parallax effects - Reset to (0, 0) to disable offset

    Example:

    // Camera scrolling\ncamera.setPosition(playerX - 64, playerY - 64);\nrenderer.setDisplayOffset(-camera.getX(), -camera.getY());\nrenderer.drawTileMap(background, 0, 0, Color::White);\n

    "},{"location":"api_reference/graphics/renderer/#void-setdisplaysizeint-w-int-h","title":"void setDisplaySize(int w, int h)","text":"

    Sets the logical display size.

    Parameters: - w (int): Width in pixels - h (int): Height in pixels

    Notes: - Typically set via DisplayConfig during construction - Use this to change display size at runtime if needed

    "},{"location":"api_reference/graphics/renderer/#int-getwidth-const","title":"int getWidth() const","text":"

    Gets the display width.

    Returns: - int: Display width in pixels

    "},{"location":"api_reference/graphics/renderer/#int-getheight-const","title":"int getHeight() const","text":"

    Gets the display height.

    Returns: - int: Display height in pixels

    "},{"location":"api_reference/graphics/renderer/#int-getxoffset-const","title":"int getXOffset() const","text":"

    Gets the current X display offset.

    Returns: - int: X offset in pixels

    "},{"location":"api_reference/graphics/renderer/#int-getyoffset-const","title":"int getYOffset() const","text":"

    Gets the current Y display offset.

    Returns: - int: Y offset in pixels

    "},{"location":"api_reference/graphics/renderer/#void-setcontrastuint8_t-level","title":"void setContrast(uint8_t level)","text":"

    Sets the display contrast (brightness).

    Parameters: - level (uint8_t): Contrast level (0-255)

    Notes: - Platform-specific: may not be supported on all displays - Higher values = brighter display

    "},{"location":"api_reference/graphics/renderer/#void-setfontconst-uint8_t-font","title":"void setFont(const uint8_t* font)","text":"

    Sets the font for text rendering.

    Parameters: - font (const uint8_t*): Pointer to the font data

    Notes: - Sets the default font for drawText() calls without font parameter - Use font constants like FONT_5X7 from Font.h

    "},{"location":"api_reference/graphics/renderer/#usage-example","title":"Usage Example","text":"
    #include \"graphics/Renderer.h\"\n#include \"graphics/DisplayConfig.h\"\n\nvoid draw(Renderer& renderer) override {\n    renderer.beginFrame();\n\n    // Draw background\n    renderer.drawFilledRectangle(0, 0, 128, 128, Color::Black);\n\n    // Draw sprites\n    renderer.drawSprite(playerSprite, playerX, playerY, Color::White);\n    renderer.drawSprite(enemySprite, enemyX, enemyY, Color::Red);\n\n    // Draw UI\n    renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n    renderer.drawTextCentered(\"Game Over\", 64, Color::Yellow, 2);\n\n    renderer.endFrame();\n}\n
    "},{"location":"api_reference/graphics/renderer/#performance-considerations","title":"Performance Considerations","text":"
    • Integer-only math: All operations use integer arithmetic for ESP32 efficiency
    • Sprite storage: Store sprite data in flash (const/constexpr) for best performance
    • Batch operations: Group similar draw calls together
    • Tilemaps: Dibuja un mapa de tiles completo. Implementa viewport culling autom\u00e1tico y cach\u00e9 de paleta para m\u00e1ximo rendimiento.
    • Sprites 2bpp/4bpp: Optimizado para ESP32 (IRAM + acceso de 16 bits).
    "},{"location":"api_reference/graphics/renderer/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Sprite data should be in flash, not RAM
    • Frame rate: Limit draw calls per frame for consistent FPS
    • Display offset: Use for scrolling instead of redrawing everything
    "},{"location":"api_reference/graphics/renderer/#see-also","title":"See Also","text":"
    • Sprite - Sprite structure
    • MultiSprite - Multi-layer sprites
    • TileMap - Tilemap structure
    • Color - Color constants
    • DisplayConfig - Display configuration
    • Camera2D - Camera for scrolling
    • Manual - Basic Rendering
    • Manual - Sprites and Animation
    • API Overview
    "},{"location":"api_reference/graphics/sprite/","title":"Sprite","text":"

    Low-level bitmap descriptor and multi-layer composition for retro rendering.

    "},{"location":"api_reference/graphics/sprite/#description","title":"Description","text":"

    Sprites are the fundamental graphics primitive in PixelRoot32. The engine supports multiple sprite formats:

    • 1bpp (Standard): Monochrome sprites, most memory-efficient
    • 2bpp (Experimental): 4 colors per sprite
    • 4bpp (Experimental): 16 colors per sprite
    • MultiSprite: Multi-layer 1bpp sprites for multi-color effects
    "},{"location":"api_reference/graphics/sprite/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    struct Sprite {\n        // ...\n    };\n\n    struct MultiSprite {\n        // ...\n    };\n\n    struct SpriteLayer {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/sprite/#sprite-structure-1bpp","title":"Sprite Structure (1bpp)","text":"

    Compact sprite descriptor for monochrome bitmapped sprites.

    "},{"location":"api_reference/graphics/sprite/#members","title":"Members","text":"
    • const uint16_t* data: Pointer to packed row data (size = height)
    • uint8_t width: Sprite width in pixels (\u2264 16)
    • uint8_t height: Sprite height in pixels
    "},{"location":"api_reference/graphics/sprite/#data-format","title":"Data Format","text":"

    Sprites are stored as an array of 16-bit rows. Each row packs horizontal pixels into bits:

    • Bit 0: Leftmost pixel of the row
    • Bit (width - 1): Rightmost pixel of the row
    • Bit value 1: Pixel on (colored)
    • Bit value 0: Pixel off (transparent/background)

    Only the lowest width bits of each row are used.

    "},{"location":"api_reference/graphics/sprite/#example","title":"Example","text":"
    // 8x8 sprite (smiley face)\nstatic const uint16_t SMILEY_DATA[] = {\n    0b00111100,  // Row 0:  \u2588\u2588\u2588\u2588\n    0b01111110,  // Row 1:  \u2588\u2588\u2588\u2588\u2588\u2588\n    0b11011011,  // Row 2:  \u2588\u2588 \u2588\u2588 \u2588\u2588\n    0b11111111,  // Row 3:  \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\n    0b11011011,  // Row 4:  \u2588\u2588 \u2588\u2588 \u2588\u2588\n    0b01100110,  // Row 5:  \u2588\u2588  \u2588\u2588\n    0b01111110,  // Row 6:  \u2588\u2588\u2588\u2588\u2588\u2588\n    0b00111100   // Row 7:  \u2588\u2588\u2588\u2588\n};\n\nstatic const Sprite SMILEY_SPRITE = {\n    SMILEY_DATA,\n    8,  // width\n    8   // height\n};\n
    "},{"location":"api_reference/graphics/sprite/#spritelayer-structure","title":"SpriteLayer Structure","text":"

    Single monochrome layer used by layered sprites.

    "},{"location":"api_reference/graphics/sprite/#members_1","title":"Members","text":"
    • const uint16_t* data: Pointer to packed row data for this layer
    • Color color: Color used for \"on\" pixels in this layer
    "},{"location":"api_reference/graphics/sprite/#notes","title":"Notes","text":"
    • Each layer uses the same width/height as its owning MultiSprite
    • Layers can have different colors
    • Layers are drawn in array order
    "},{"location":"api_reference/graphics/sprite/#multisprite-structure","title":"MultiSprite Structure","text":"

    Multi-layer, multi-color sprite built from 1bpp layers.

    "},{"location":"api_reference/graphics/sprite/#members_2","title":"Members","text":"
    • uint8_t width: Sprite width in pixels (\u2264 16)
    • uint8_t height: Sprite height in pixels
    • const SpriteLayer* layers: Pointer to array of layers
    • uint8_t layerCount: Number of layers in the array
    "},{"location":"api_reference/graphics/sprite/#notes_1","title":"Notes","text":"
    • Combines several 1bpp layers with different colors
    • Layers are drawn in array order
    • Enables multi-color sprites without higher bit-depths
    • More layers = more draw calls (but still efficient)
    "},{"location":"api_reference/graphics/sprite/#example_1","title":"Example","text":"
    // Outline layer\nstatic const uint16_t OUTLINE_DATA[] = {\n    0b11111111,\n    0b10000001,\n    0b10000001,\n    0b11111111\n};\n\n// Fill layer\nstatic const uint16_t FILL_DATA[] = {\n    0b00000000,\n    0b01111110,\n    0b01111110,\n    0b00000000\n};\n\nstatic const SpriteLayer LAYERS[] = {\n    {OUTLINE_DATA, Color::Black},  // Layer 0: Black outline\n    {FILL_DATA, Color::Red}       // Layer 1: Red fill\n};\n\nstatic const MultiSprite PLAYER_MULTISPRITE = {\n    8,      // width\n    8,      // height\n    LAYERS, // layers array\n    2       // layer count\n};\n
    "},{"location":"api_reference/graphics/sprite/#sprite2bpp-structure-experimental","title":"Sprite2bpp Structure (Experimental)","text":"

    2-bit per pixel sprite (4 colors).

    Requires: PIXELROOT32_ENABLE_2BPP_SPRITES build flag

    "},{"location":"api_reference/graphics/sprite/#members_3","title":"Members","text":"
    • const uint16_t* data: Datos empaquetados (4 p\u00edxeles por cada 8 bits, alineados a 16 bits)
    • const Color* palette: Local palette (4 colors)
    • uint8_t width: Sprite width
    • uint8_t height: Sprite height
    • uint8_t paletteSize: Number of colors (typically 4)
    "},{"location":"api_reference/graphics/sprite/#notes_2","title":"Notes","text":"
    • Experimental feature
    • Uses more memory than 1bpp
    • Each pixel can be one of 4 colors from local palette
    "},{"location":"api_reference/graphics/sprite/#sprite4bpp-structure-experimental","title":"Sprite4bpp Structure (Experimental)","text":"

    4-bit per pixel sprite (16 colors).

    Requires: PIXELROOT32_ENABLE_4BPP_SPRITES build flag

    "},{"location":"api_reference/graphics/sprite/#members_4","title":"Members","text":"
    • const uint16_t* data: Datos empaquetados (2 p\u00edxeles por cada 8 bits, alineados a 16 bits)
    • const Color* palette: Local palette (16 colors)
    • uint8_t width: Sprite width
    • uint8_t height: Sprite height
    • uint8_t paletteSize: Number of colors (typically 16)
    "},{"location":"api_reference/graphics/sprite/#notes_3","title":"Notes","text":"
    • Experimental feature
    • Uses more memory than 1bpp/2bpp
    • Each pixel can be one of 16 colors from local palette
    "},{"location":"api_reference/graphics/sprite/#sprite-animation","title":"Sprite Animation","text":""},{"location":"api_reference/graphics/sprite/#spriteanimationframe-structure","title":"SpriteAnimationFrame Structure","text":"

    Frame that can reference either a Sprite or a MultiSprite.

    Members: - const Sprite* sprite: Optional pointer to a simple 1bpp sprite frame - const MultiSprite* multiSprite: Optional pointer to a layered sprite frame

    Notes: - Exactly one pointer should be non-null for a valid frame - Allows same animation system for both sprite types

    "},{"location":"api_reference/graphics/sprite/#spriteanimation-structure","title":"SpriteAnimation Structure","text":"

    Lightweight, step-based sprite animation controller.

    Members: - const SpriteAnimationFrame* frames: Pointer to immutable frame table - uint8_t frameCount: Number of frames in the table - uint8_t current: Current frame index [0, frameCount)

    Methods: - void reset(): Reset to first frame - void step(): Advance to next frame (wrapping) - const SpriteAnimationFrame& getCurrentFrame() const: Get current frame - const Sprite* getCurrentSprite() const: Get current simple sprite - const MultiSprite* getCurrentMultiSprite() const: Get current multi-sprite

    Example:

    static const SpriteAnimationFrame WALK_FRAMES[] = {\n    {&walkFrame1, nullptr},\n    {&walkFrame2, nullptr},\n    {&walkFrame3, nullptr},\n    {&walkFrame2, nullptr}  // Loop back\n};\n\nstatic SpriteAnimation walkAnimation = {\n    WALK_FRAMES,\n    4,    // frameCount\n    0     // current\n};\n\n// In update\nwalkAnimation.step();\nconst Sprite* currentSprite = walkAnimation.getCurrentSprite();\n

    "},{"location":"api_reference/graphics/sprite/#usage-examples","title":"Usage Examples","text":""},{"location":"api_reference/graphics/sprite/#creating-1bpp-sprites","title":"Creating 1bpp Sprites","text":"
    // 8x8 player sprite\nstatic const uint16_t PLAYER_DATA[] = {\n    0b00111100,\n    0b01111110,\n    0b11111111,\n    0b11011011,\n    0b11111111,\n    0b01111110,\n    0b00111100,\n    0b00011000\n};\n\nstatic const Sprite PLAYER_SPRITE = {\n    PLAYER_DATA,\n    8,  // width\n    8   // height\n};\n\n// Use in rendering\nrenderer.drawSprite(PLAYER_SPRITE, 100, 100, Color::White);\n
    "},{"location":"api_reference/graphics/sprite/#creating-multisprite","title":"Creating MultiSprite","text":"
    // Outline layer\nstatic const uint16_t OUTLINE[] = {\n    0b11111111,\n    0b10000001,\n    0b10000001,\n    0b11111111\n};\n\n// Fill layer\nstatic const uint16_t FILL[] = {\n    0b00000000,\n    0b01111110,\n    0b01111110,\n    0b00000000\n};\n\nstatic const SpriteLayer LAYERS[] = {\n    {OUTLINE, Color::Black},\n    {FILL, Color::Red}\n};\n\nstatic const MultiSprite ENEMY_SPRITE = {\n    8,      // width\n    8,      // height\n    LAYERS, // layers\n    2       // layer count\n};\n\n// Use in rendering\nrenderer.drawMultiSprite(ENEMY_SPRITE, 100, 100);\n
    "},{"location":"api_reference/graphics/sprite/#sprite-animation_1","title":"Sprite Animation","text":"
    class AnimatedActor : public pixelroot32::core::Actor {\nprivate:\n    SpriteAnimation animation;\n    unsigned long animTimer = 0;\n    unsigned long animInterval = 200;  // 200ms per frame\n\npublic:\n    void update(unsigned long deltaTime) override {\n        Actor::update(deltaTime);\n\n        animTimer += deltaTime;\n        if (animTimer >= animInterval) {\n            animTimer -= animInterval;\n            animation.step();\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        const Sprite* frame = animation.getCurrentSprite();\n        if (frame) {\n            renderer.drawSprite(*frame, \n                               static_cast<int>(x), \n                               static_cast<int>(y), \n                               Color::White);\n        }\n    }\n};\n
    "},{"location":"api_reference/graphics/sprite/#sprite-flipping","title":"Sprite Flipping","text":"

    Sprites can be flipped horizontally:

    // Draw normal\nrenderer.drawSprite(sprite, 100, 100, Color::White, false);\n\n// Draw flipped\nrenderer.drawSprite(sprite, 100, 100, Color::White, true);\n
    "},{"location":"api_reference/graphics/sprite/#performance-considerations","title":"Performance Considerations","text":"
    • 1bpp sprites: Most efficient (integer-only operations)
    • MultiSprite: Each layer is a separate draw call (still efficient)
    • 2bpp/4bpp: Experimental, uses more memory and CPU
    • Storage: Store sprite data in flash (const/constexpr) for best performance
    • Size limit: Sprites are limited to 16 pixels wide for 1bpp format
    "},{"location":"api_reference/graphics/sprite/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Store sprite data in flash, not RAM
    • Sprite size: Smaller sprites = faster drawing
    • Format choice: Use 1bpp when possible for best performance
    • MultiSprite: More layers = more draw calls (but acceptable)
    "},{"location":"api_reference/graphics/sprite/#see-also","title":"See Also","text":"
    • Renderer - Rendering system that draws sprites
    • Color - Color constants for sprites
    • Manual - Sprites and Animation
    • API Overview
    "},{"location":"api_reference/graphics/tilemap/","title":"TileMap","text":"

    Generic structure for tile-based background rendering.

    "},{"location":"api_reference/graphics/tilemap/#description","title":"Description","text":"

    TileMapGeneric<T> is a template structure for rendering tile-based backgrounds efficiently. It supports multiple bit-depths (1bpp, 2bpp, 4bpp) by using the appropriate sprite type for tiles.

    Tilemaps are ideal for large backgrounds, levels, and static environments. They support viewport culling (only visible tiles are drawn) for optimal performance.

    "},{"location":"api_reference/graphics/tilemap/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    template<typename T>\n    struct TileMapGeneric {\n        // ...\n    };\n\n    using TileMap = TileMapGeneric<Sprite>;\n    using TileMap2bpp = TileMapGeneric<Sprite2bpp>;\n    using TileMap4bpp = TileMapGeneric<Sprite4bpp>;\n}\n
    "},{"location":"api_reference/graphics/tilemap/#template-parameters","title":"Template Parameters","text":""},{"location":"api_reference/graphics/tilemap/#t","title":"T","text":"

    The sprite type used for tiles.

    Supported types: - Sprite (1bpp) - Sprite2bpp (2bpp) - Sprite4bpp (4bpp)

    "},{"location":"api_reference/graphics/tilemap/#structure","title":"Structure","text":""},{"location":"api_reference/graphics/tilemap/#uint8_t-indices","title":"uint8_t* indices","text":"

    Array of tile indices mapping to tile sprites.

    Type: uint8_t*

    Access: Read-write

    Notes: - Array size = width * height - Each value is an index into the tiles array - 0 = first tile, 1 = second tile, etc. - Should be stored in flash (const) for best performance

    Example:

    // 16x16 tilemap (256 tiles)\nstatic const uint8_t LEVEL_INDICES[] = {\n    0, 0, 0, 0, 1, 1, 1, 1, // Row 0\n    0, 2, 2, 2, 2, 2, 2, 0, // Row 1\n    // ... more rows\n};\n

    "},{"location":"api_reference/graphics/tilemap/#uint8_t-width","title":"uint8_t width","text":"

    Width of the tilemap in tiles.

    Type: uint8_t

    Access: Read-write

    Example:

    width = 16;  // 16 tiles wide\n

    "},{"location":"api_reference/graphics/tilemap/#uint8_t-height","title":"uint8_t height","text":"

    Height of the tilemap in tiles.

    Type: uint8_t

    Access: Read-write

    Example:

    height = 16;  // 16 tiles tall\n

    "},{"location":"api_reference/graphics/tilemap/#const-t-tiles","title":"const T* tiles","text":"

    Array of tile sprites of type T.

    Type: const T*

    Access: Read-only

    Notes: - Array of sprite pointers, one per unique tile - Indices reference this array - All tiles should be the same size - Should be stored in flash (const) for best performance

    Example (1bpp):

    static const Sprite TILE_SPRITES[] = {\n    EMPTY_TILE,   // Index 0\n    WALL_TILE,    // Index 1\n    FLOOR_TILE,   // Index 2\n    // ... more tiles\n};\n

    Example (2bpp):

    static const Sprite2bpp TILE_SPRITES_2BPP[] = {\n    TILE_GRASS,   // Index 0\n    TILE_DIRT,    // Index 1\n    // ... more tiles\n};\n

    "},{"location":"api_reference/graphics/tilemap/#uint8_t-tilewidth","title":"uint8_t tileWidth","text":"

    Width of each tile in pixels.

    Type: uint8_t

    Access: Read-write

    Notes: - All tiles must have the same width - Common values: 8, 16 pixels - Should match sprite width

    Example:

    tileWidth = 8;  // 8x8 tiles\n

    "},{"location":"api_reference/graphics/tilemap/#uint8_t-tileheight","title":"uint8_t tileHeight","text":"

    Height of each tile in pixels.

    Type: uint8_t

    Access: Read-write

    Notes: - All tiles must have the same height - Common values: 8, 16 pixels - Should match sprite height

    Example:

    tileHeight = 8;  // 8x8 tiles\n

    "},{"location":"api_reference/graphics/tilemap/#uint16_t-tilecount","title":"uint16_t tileCount","text":"

    Number of unique tiles in the tiles array.

    Type: uint16_t

    Access: Read-write

    Notes: - Must match the size of the tiles array - Indices must be < tileCount

    Example:

    tileCount = 16;  // 16 unique tiles\n

    "},{"location":"api_reference/graphics/tilemap/#creating-tilemaps","title":"Creating Tilemaps","text":""},{"location":"api_reference/graphics/tilemap/#step-1-create-tile-sprites","title":"Step 1: Create Tile Sprites","text":"
    // Empty tile (index 0)\nstatic const uint16_t EMPTY_DATA[] = {\n    0b00000000,\n    0b00000000,\n    0b00000000,\n    0b00000000,\n    0b00000000,\n    0b00000000,\n    0b00000000,\n    0b00000000\n};\n\n// Wall tile (index 1)\nstatic const uint16_t WALL_DATA[] = {\n    0b11111111,\n    0b10000001,\n    0b10000001,\n    0b10000001,\n    0b10000001,\n    0b10000001,\n    0b10000001,\n    0b11111111\n};\n\n// Floor tile (index 2)\nstatic const uint16_t FLOOR_DATA[] = {\n    0b00000000,\n    0b01111110,\n    0b01111110,\n    0b01111110,\n    0b01111110,\n    0b01111110,\n    0b01111110,\n    0b00000000\n};\n\nstatic const Sprite TILE_SPRITES[] = {\n    {EMPTY_DATA, 8, 8},  // Index 0\n    {WALL_DATA, 8, 8},   // Index 1\n    {FLOOR_DATA, 8, 8}   // Index 2\n};\n
    "},{"location":"api_reference/graphics/tilemap/#step-2-create-index-array","title":"Step 2: Create Index Array","text":"
    // 16x16 level (256 tiles)\n// 0 = empty, 1 = wall, 2 = floor\nstatic const uint8_t LEVEL_INDICES[] = {\n    // Row 0: Top wall\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n    // Row 1: Walls on sides\n    1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,\n    // Row 2\n    1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,\n    // ... more rows\n    // Row 15: Bottom wall\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1\n};\n
    "},{"location":"api_reference/graphics/tilemap/#step-3-create-tilemap-structure","title":"Step 3: Create TileMap Structure","text":"
    static const TileMap LEVEL_MAP = {\n    const_cast<uint8_t*>(LEVEL_INDICES),  // indices (non-const for struct)\n    16,        // width in tiles\n    16,        // height in tiles\n    TILE_SPRITES, // tile sprites array\n    8,         // tile width\n    8,         // tile height\n    3          // tile count\n};\n
    "},{"location":"api_reference/graphics/tilemap/#rendering-tilemaps","title":"Rendering Tilemaps","text":"

    Use Renderer::drawTileMap():

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Draw tilemap at origin (0, 0)\n    renderer.drawTileMap(LEVEL_MAP, 0, 0, Color::White);\n\n    // With camera offset\n    camera.apply(renderer);\n    renderer.drawTileMap(LEVEL_MAP, 0, 0, Color::White);\n}\n
    "},{"location":"api_reference/graphics/tilemap/#viewport-culling","title":"Viewport Culling","text":"

    Tilemaps automatically cull tiles outside the viewport:

    • Only visible tiles are drawn
    • Very efficient for large levels
    • Works with camera scrolling

    Example:

    // Large level (256x256 tiles)\n// Only tiles visible on screen are drawn\ncamera.apply(renderer);\nrenderer.drawTileMap(LARGE_LEVEL_MAP, 0, 0, Color::White);\n

    "},{"location":"api_reference/graphics/tilemap/#collision-detection-with-tilemaps","title":"Collision Detection with Tilemaps","text":"

    Check tile at world position:

    bool isSolidTile(int worldX, int worldY, const TileMap& map) {\n    int tileX = worldX / map.tileWidth;\n    int tileY = worldY / map.tileHeight;\n\n    if (tileX < 0 || tileX >= map.width || \n        tileY < 0 || tileY >= map.height) {\n        return true;  // Outside map = solid\n    }\n\n    int index = tileY * map.width + tileX;\n    uint8_t tileIndex = map.indices[index];\n\n    // Check if tile is solid (e.g., wall = index 1)\n    return (tileIndex == 1);\n}\n
    "},{"location":"api_reference/graphics/tilemap/#usage-example","title":"Usage Example","text":"
    #include \"graphics/TileMap.h\"\n#include \"graphics/Renderer.h\"\n\nclass LevelScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::TileMap levelMap;\n    pixelroot32::graphics::Camera2D camera;\n\npublic:\n    void init() override {\n        // Level map is already defined (see above)\n        // Create camera\n        auto& renderer = engine.getRenderer();\n        camera = pixelroot32::graphics::Camera2D(\n            renderer.getWidth(),\n            renderer.getHeight()\n        );\n\n        // Set level boundaries\n        int levelWidth = levelMap.width * levelMap.tileWidth;\n        int levelHeight = levelMap.height * levelMap.tileHeight;\n        camera.setBounds(0.0f, levelWidth - renderer.getWidth());\n        camera.setVerticalBounds(0.0f, levelHeight - renderer.getHeight());\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Camera follows player\n        if (player) {\n            camera.followTarget(player->x, player->y);\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Apply camera\n        camera.apply(renderer);\n\n        // Draw tilemap (viewport culling automatic)\n        renderer.drawTileMap(levelMap, 0, 0, Color::White);\n\n        // Draw entities\n        Scene::draw(renderer);\n\n        // Reset for UI\n        renderer.setDisplayOffset(0, 0);\n    }\n\n    bool checkTileCollision(float x, float y) {\n        int tileX = static_cast<int>(x) / levelMap.tileWidth;\n        int tileY = static_cast<int>(y) / levelMap.tileHeight;\n\n        if (tileX < 0 || tileX >= levelMap.width || \n            tileY < 0 || tileY >= levelMap.height) {\n            return true;  // Outside = solid\n        }\n\n        int index = tileY * levelMap.width + tileX;\n        uint8_t tile = levelMap.indices[index];\n        return (tile == 1);  // Wall tile\n    }\n};\n
    "},{"location":"api_reference/graphics/tilemap/#performance-considerations","title":"Performance Considerations","text":"
    • Viewport culling: Only visible tiles are drawn (automatic)
    • Tile reuse: Reuse tile sprites across the map
    • Index storage: Compact uint8_t indices (1 byte per tile)
    • Memory: Store indices and tiles in flash (const) for best performance
    • Tile size: Smaller tiles = more tiles to draw, but more detail
    "},{"location":"api_reference/graphics/tilemap/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Store tilemap data in flash, not RAM
    • Map size: Large maps use more flash memory
    • Tile count: Limit unique tiles to save memory
    • Culling: Viewport culling is essential for large levels
    "},{"location":"api_reference/graphics/tilemap/#see-also","title":"See Also","text":"
    • Renderer - Rendering system that draws tilemaps
    • Sprite - Sprite structure used for tiles
    • Camera2D - Camera for scrolling tilemaps
    • Manual - Tilemaps
    • API Overview
    "},{"location":"api_reference/physics/collision_system/","title":"CollisionSystem","text":"

    Manages collision detection between entities.

    "},{"location":"api_reference/physics/collision_system/#description","title":"Description","text":"

    CollisionSystem iterates through registered entities, checks if they are Actors, and performs AABB (Axis-Aligned Bounding Box) collision checks based on their collision layers and masks.

    The system automatically filters collisions using layers and masks, avoiding unnecessary checks. When a collision is detected, it triggers the onCollision() callback on both actors.

    "},{"location":"api_reference/physics/collision_system/#namespace","title":"Namespace","text":"
    namespace pixelroot32::physics {\n    class CollisionSystem {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/physics/collision_system/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Scene (manages collision system instance)
    "},{"location":"api_reference/physics/collision_system/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/physics/collision_system/#void-addentityentity-e","title":"void addEntity(Entity* e)","text":"

    Adds an entity to the collision system.

    Parameters: - e (pixelroot32::core::Entity*): Pointer to the entity to add

    Returns: - void

    Notes: - Only Actor entities participate in collisions - Entities are automatically added when added to Scene - Typically not called directly (handled by Scene)

    Example:

    // Typically handled automatically by Scene\nscene->addEntity(actor);  // Automatically added to collision system\n

    "},{"location":"api_reference/physics/collision_system/#void-removeentityentity-e","title":"void removeEntity(Entity* e)","text":"

    Removes an entity from the collision system.

    Parameters: - e (pixelroot32::core::Entity*): Pointer to the entity to remove

    Returns: - void

    Notes: - Entities are automatically removed when removed from Scene - Typically not called directly

    Example:

    // Typically handled automatically by Scene\nscene->removeEntity(actor);  // Automatically removed from collision system\n

    "},{"location":"api_reference/physics/collision_system/#void-update","title":"void update()","text":"

    Performs collision detection for all registered entities.

    Returns: - void

    Notes: - Called automatically by Scene::update() - Iterates through all pairs of entities - Only checks collisions between Actors with matching layers/masks - Triggers onCollision() callbacks when collisions are detected - Uses AABB (Axis-Aligned Bounding Box) collision detection

    Example:

    // Called automatically by Scene, but can be called manually:\nvoid update(unsigned long deltaTime) override {\n    Scene::update(deltaTime);  // Calls collisionSystem.update()\n\n    // Or manually:\n    collisionSystem.update();\n}\n

    "},{"location":"api_reference/physics/collision_system/#how-it-works","title":"How It Works","text":"
    1. Entity Registration: Entities added to Scene are automatically registered
    2. Layer Filtering: Only actors with matching layers/masks are checked
    3. AABB Check: Uses Rect::intersects() for collision detection
    4. Callback: Calls onCollision() on both actors when collision detected
    "},{"location":"api_reference/physics/collision_system/#collision-detection-algorithm","title":"Collision Detection Algorithm","text":"
    for each actor1 in entities:\n    if actor1 is not an Actor: continue\n    for each actor2 in entities:\n        if actor2 is not an Actor: continue\n        if actor1 == actor2: continue\n\n        // Check layers/masks\n        if (actor1->layer & actor2->mask) == 0: continue\n        if (actor2->layer & actor1->mask) == 0: continue\n\n        // Check AABB intersection\n        Rect box1 = actor1->getHitBox();\n        Rect box2 = actor2->getHitBox();\n\n        if (box1.intersects(box2)) {\n            actor1->onCollision(actor2);\n            actor2->onCollision(actor1);\n        }\n
    "},{"location":"api_reference/physics/collision_system/#usage-example","title":"Usage Example","text":"
    #include \"physics/CollisionSystem.h\"\n#include \"core/Actor.h\"\n\nclass GameScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Create actors with collision layers\n        PlayerActor* player = new PlayerActor(64, 64);\n        player->layer = pixelroot32::physics::DefaultLayers::kPlayer;\n        player->mask = pixelroot32::physics::DefaultLayers::kEnemy | \n                       pixelroot32::physics::DefaultLayers::kObstacle;\n        addEntity(player);\n\n        EnemyActor* enemy = new EnemyActor(100, 100);\n        enemy->layer = pixelroot32::physics::DefaultLayers::kEnemy;\n        enemy->mask = pixelroot32::physics::DefaultLayers::kPlayer;\n        addEntity(enemy);\n\n        // Collision system is managed by Scene\n        // Collisions are checked automatically in Scene::update()\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Collision detection happens here automatically\n        Scene::update(deltaTime);\n    }\n};\n
    "},{"location":"api_reference/physics/collision_system/#performance-considerations","title":"Performance Considerations","text":"
    • Layer filtering: Very efficient; avoids most collision checks
    • AABB checks: Fast (simple rectangle intersection)
    • Pair checking: O(n\u00b2) complexity, but n is limited (MAX_ENTITIES = 32)
    • Update frequency: Called every frame; keep hitboxes simple
    "},{"location":"api_reference/physics/collision_system/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Entity limit: MAX_ENTITIES = 32 limits collision pairs
    • Layer efficiency: Use layers effectively to minimize checks
    • Hitbox simplicity: Keep hitboxes as simple AABB for best performance
    "},{"location":"api_reference/physics/collision_system/#see-also","title":"See Also","text":"
    • Actor - Actors that participate in collisions
    • CollisionTypes - Collision primitives and layers
    • Manual - Physics and Collisions
    • API Overview
    "},{"location":"api_reference/physics/collision_types/","title":"Collision Types","text":"

    Basic geometric types and collision layer definitions.

    "},{"location":"api_reference/physics/collision_types/#description","title":"Description","text":"

    This document describes the collision primitives (Rect, Circle, Segment) and collision layer system used by the collision detection system.

    "},{"location":"api_reference/physics/collision_types/#namespace","title":"Namespace","text":"
    namespace pixelroot32::physics {\n    // Types and functions\n}\n
    "},{"location":"api_reference/physics/collision_types/#collisionlayer-type","title":"CollisionLayer Type","text":"

    Collision layer type (bit flags).

    Type: uint16_t (typedef)

    Notes: - Uses bit flags for layer assignment - Actors can be on multiple layers (bitwise OR) - Masks define which layers an actor can collide with

    "},{"location":"api_reference/physics/collision_types/#defaultlayers-namespace","title":"DefaultLayers Namespace","text":"

    Predefined collision layers.

    "},{"location":"api_reference/physics/collision_types/#knone","title":"kNone","text":"

    No layer (0).

    Value: 0

    Usage:

    actor->layer = pixelroot32::physics::DefaultLayers::kNone;\n

    "},{"location":"api_reference/physics/collision_types/#kall","title":"kAll","text":"

    All layers (0xFFFF).

    Value: 0xFFFF

    Usage:

    actor->mask = pixelroot32::physics::DefaultLayers::kAll;  // Collide with everything\n

    "},{"location":"api_reference/physics/collision_types/#custom-layers","title":"Custom Layers","text":"

    Define custom layers using bit flags:

    namespace MyLayers {\n    const pixelroot32::physics::CollisionLayer kPlayer = 1 << 0;      // Bit 0\n    const pixelroot32::physics::CollisionLayer kEnemy = 1 << 1;       // Bit 1\n    const pixelroot32::physics::CollisionLayer kObstacle = 1 << 2;    // Bit 2\n    const pixelroot32::physics::CollisionLayer kProjectile = 1 << 3;  // Bit 3\n    const pixelroot32::physics::CollisionLayer kCollectible = 1 << 4;  // Bit 4\n}\n\n// Usage\nactor->layer = MyLayers::kPlayer;\nactor->mask = MyLayers::kEnemy | MyLayers::kObstacle;\n
    "},{"location":"api_reference/physics/collision_types/#circle-structure","title":"Circle Structure","text":"

    Represents a circle for collision detection.

    Members: - float x: Center X coordinate - float y: Center Y coordinate - float radius: Radius of the circle

    Example:

    pixelroot32::physics::Circle circle;\ncircle.x = 100.0f;\ncircle.y = 100.0f;\ncircle.radius = 10.0f;\n

    "},{"location":"api_reference/physics/collision_types/#segment-structure","title":"Segment Structure","text":"

    Represents a line segment for collision detection.

    Members: - float x1, y1: Start point coordinates - float x2, y2: End point coordinates

    Example:

    pixelroot32::physics::Segment segment;\nsegment.x1 = 0.0f;\nsegment.y1 = 0.0f;\nsegment.x2 = 100.0f;\nsegment.y2 = 100.0f;\n

    "},{"location":"api_reference/physics/collision_types/#intersection-functions","title":"Intersection Functions","text":""},{"location":"api_reference/physics/collision_types/#bool-intersectsconst-circle-a-const-circle-b","title":"bool intersects(const Circle& a, const Circle& b)","text":"

    Checks if two circles intersect.

    Parameters: - a (const Circle&): First circle - b (const Circle&): Second circle

    Returns: - bool: true if circles intersect

    Example:

    pixelroot32::physics::Circle circle1{100.0f, 100.0f, 10.0f};\npixelroot32::physics::Circle circle2{110.0f, 100.0f, 10.0f};\n\nif (pixelroot32::physics::intersects(circle1, circle2)) {\n    // Circles overlap\n}\n

    "},{"location":"api_reference/physics/collision_types/#bool-intersectsconst-circle-c-const-rect-r","title":"bool intersects(const Circle& c, const Rect& r)","text":"

    Checks if a circle and rectangle intersect.

    Parameters: - c (const Circle&): Circle - r (const Rect&): Rectangle

    Returns: - bool: true if circle and rectangle intersect

    Example:

    pixelroot32::physics::Circle circle{100.0f, 100.0f, 10.0f};\npixelroot32::core::Rect rect{95.0f, 95.0f, 20, 20};\n\nif (pixelroot32::physics::intersects(circle, rect)) {\n    // Circle overlaps rectangle\n}\n

    "},{"location":"api_reference/physics/collision_types/#bool-intersectsconst-segment-s-const-rect-r","title":"bool intersects(const Segment& s, const Rect& r)","text":"

    Checks if a line segment and rectangle intersect.

    Parameters: - s (const Segment&): Line segment - r (const Rect&): Rectangle

    Returns: - bool: true if segment and rectangle intersect

    Example:

    pixelroot32::physics::Segment segment{0.0f, 0.0f, 100.0f, 100.0f};\npixelroot32::core::Rect rect{50.0f, 50.0f, 20, 20};\n\nif (pixelroot32::physics::intersects(segment, rect)) {\n    // Segment intersects rectangle\n}\n

    "},{"location":"api_reference/physics/collision_types/#bool-sweepcirclevsrectconst-circle-start-const-circle-end-const-rect-target-float-thit","title":"bool sweepCircleVsRect(const Circle& start, const Circle& end, const Rect& target, float& tHit)","text":"

    Performs a sweep test: checks if a moving circle collides with a rectangle.

    Parameters: - start (const Circle&): Circle at start position - end (const Circle&): Circle at end position - target (const Rect&): Target rectangle - tHit (float&): Output parameter for collision time (0.0 to 1.0)

    Returns: - bool: true if collision occurs during sweep

    Notes: - Useful for fast-moving projectiles - tHit indicates when collision occurs (0.0 = start, 1.0 = end) - More expensive than simple AABB check

    Example:

    // Projectile moving from (0, 0) to (100, 100)\npixelroot32::physics::Circle start{0.0f, 0.0f, 5.0f};\npixelroot32::physics::Circle end{100.0f, 100.0f, 5.0f};\npixelroot32::core::Rect wall{50.0f, 50.0f, 20, 20};\n\nfloat tHit = 0.0f;\nif (pixelroot32::physics::sweepCircleVsRect(start, end, wall, tHit)) {\n    // Collision at time tHit\n    float hitX = 0.0f + (100.0f - 0.0f) * tHit;\n    float hitY = 0.0f + (100.0f - 0.0f) * tHit;\n}\n

    "},{"location":"api_reference/physics/collision_types/#rect-structure-from-coreentityh","title":"Rect Structure (from core/Entity.h)","text":"

    Represents a 2D rectangle for collision detection.

    Members: - float x, y: Top-left corner coordinates - int width, height: Dimensions

    Methods: - bool intersects(const Rect& other) const: Checks if rectangles overlap

    Example:

    pixelroot32::core::Rect rect1{10.0f, 20.0f, 50, 50};\npixelroot32::core::Rect rect2{30.0f, 40.0f, 50, 50};\n\nif (rect1.intersects(rect2)) {\n    // Rectangles overlap\n}\n

    "},{"location":"api_reference/physics/collision_types/#usage-examples","title":"Usage Examples","text":""},{"location":"api_reference/physics/collision_types/#layer-based-collision","title":"Layer-Based Collision","text":"
    // Define layers\nnamespace GameLayers {\n    const pixelroot32::physics::CollisionLayer kPlayer = 1 << 0;\n    const pixelroot32::physics::CollisionLayer kEnemy = 1 << 1;\n    const pixelroot32::physics::CollisionLayer kObstacle = 1 << 2;\n    const pixelroot32::physics::CollisionLayer kProjectile = 1 << 3;\n}\n\n// Player collides with enemies and obstacles\nplayer->layer = GameLayers::kPlayer;\nplayer->mask = GameLayers::kEnemy | GameLayers::kObstacle;\n\n// Enemy collides with player and obstacles\nenemy->layer = GameLayers::kEnemy;\nenemy->mask = GameLayers::kPlayer | GameLayers::kObstacle;\n\n// Projectile collides with enemies only\nprojectile->layer = GameLayers::kProjectile;\nprojectile->mask = GameLayers::kEnemy;\n
    "},{"location":"api_reference/physics/collision_types/#circle-vs-circle-collision","title":"Circle vs Circle Collision","text":"
    bool checkCollision(const Circle& a, const Circle& b) {\n    return pixelroot32::physics::intersects(a, b);\n}\n
    "},{"location":"api_reference/physics/collision_types/#sweep-test-for-fast-projectiles","title":"Sweep Test for Fast Projectiles","text":"
    class ProjectileActor : public pixelroot32::core::Actor {\nprivate:\n    float startX, startY;\n    float endX, endY;\n    float radius = 2.0f;\n\npublic:\n    bool checkWallCollision(const Rect& wall) {\n        pixelroot32::physics::Circle start{startX, startY, radius};\n        pixelroot32::physics::Circle end{endX, endY, radius};\n\n        float tHit = 0.0f;\n        if (pixelroot32::physics::sweepCircleVsRect(start, end, wall, tHit)) {\n            // Collision detected at time tHit\n            return true;\n        }\n        return false;\n    }\n};\n
    "},{"location":"api_reference/physics/collision_types/#performance-considerations","title":"Performance Considerations","text":"
    • AABB checks: Very fast (simple rectangle intersection)
    • Circle checks: Slightly slower (distance calculation)
    • Sweep tests: More expensive (use only for fast-moving objects)
    • Layer filtering: Essential for performance with many actors
    "},{"location":"api_reference/physics/collision_types/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Float math: Uses floating point; acceptable but integer math would be faster
    • Sweep tests: Use sparingly (more CPU intensive)
    • Layer efficiency: Use layers effectively to minimize checks
    "},{"location":"api_reference/physics/collision_types/#see-also","title":"See Also","text":"
    • CollisionSystem - Collision detection system
    • Actor - Actors that use collision layers
    • Manual - Physics and Collisions
    • API Overview
    "},{"location":"api_reference/ui/ui_button/","title":"UIButton","text":"

    A clickable button UI element.

    "},{"location":"api_reference/ui/ui_button/#description","title":"Description","text":"

    UIButton is a clickable button that supports both physical (keyboard/gamepad) and touch input. It can trigger a callback function when pressed and integrates with UI layouts for automatic navigation.

    Buttons support selection state (for D-pad navigation), custom styling, and text alignment.

    "},{"location":"api_reference/ui/ui_button/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIButton : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_button/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    • Inherited by: Your custom button classes (if needed)
    "},{"location":"api_reference/ui/ui_button/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_button/#uibuttonstring-t-uint8_t-index-float-x-float-y-float-w-float-h-function-callback-textalignment-textalign-center-int-fontsize-2","title":"UIButton(string t, uint8_t index, float x, float y, float w, float h, function callback, TextAlignment textAlign = CENTER, int fontSize = 2)

    Constructs a new UIButton.

    Parameters: - t (std::string): Button label text - index (uint8_t): Navigation index (for D-pad navigation in layouts) - x (float): X position - y (float): Y position - w (float): Width - h (float): Height - callback (std::function): Function to call when clicked/pressed - textAlign (TextAlignment, optional): Text alignment. Default: CENTER - fontSize (int, optional): Text size multiplier. Default: 2

    Example:

    #include \"graphics/ui/UIButton.h\"\n\nvoid onStartButtonClicked() {\n    // Start game\n    engine.setScene(&gameScene);\n}\n\nvoid onQuitButtonClicked() {\n    // Quit game\n    engine.stop();\n}\n\n// Create buttons\npixelroot32::graphics::ui::UIButton* startButton = new pixelroot32::graphics::ui::UIButton(\n    \"Start\",\n    0,  // index\n    64.0f, 64.0f,  // position\n    100.0f, 30.0f, // size\n    onStartButtonClicked,\n    pixelroot32::graphics::ui::TextAlignment::CENTER,\n    2\n);\n\npixelroot32::graphics::ui::UIButton* quitButton = new pixelroot32::graphics::ui::UIButton(\n    \"Quit\",\n    1,  // index\n    64.0f, 100.0f,\n    100.0f, 30.0f,\n    onQuitButtonClicked\n);\n

    ","text":""},{"location":"api_reference/ui/ui_button/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_button/#void-setstylecolor-textcol-color-bgcol-bool-drawbg","title":"void setStyle(Color textCol, Color bgCol, bool drawBg)

    Configures the button's visual style.

    Parameters: - textCol (Color): Color of the text - bgCol (Color): Color of the background - drawBg (bool): Whether to draw the background rectangle

    Returns: - void

    Example:

    button->setStyle(\n    pixelroot32::graphics::Color::White,  // Text color\n    pixelroot32::graphics::Color::Blue,   // Background color\n    true                                   // Draw background\n);\n

    ","text":""},{"location":"api_reference/ui/ui_button/#void-setselectedbool-selected","title":"void setSelected(bool selected)

    Sets the selection state (e.g., focused via D-pad).

    Parameters: - selected (bool): true if selected

    Returns: - void

    Notes: - Used by layouts for D-pad navigation - Selected buttons typically have different visual style - Can be set manually or automatically by layouts

    Example:

    button->setSelected(true);  // Highlight button\n

    ","text":""},{"location":"api_reference/ui/ui_button/#bool-getselected-const","title":"bool getSelected() const

    Checks if the button is currently selected.

    Returns: - bool: true if selected

    Example:

    if (button->getSelected()) {\n    // Button is focused\n}\n

    ","text":""},{"location":"api_reference/ui/ui_button/#bool-isfocusable-const-override","title":"bool isFocusable() const override

    Returns true (Buttons are always focusable).

    Returns: - bool: Always true

    ","text":""},{"location":"api_reference/ui/ui_button/#void-handleinputconst-inputmanager-input","title":"void handleInput(const InputManager& input)

    Handles input events. Checks for touch events within bounds or confirmation buttons if selected.

    Parameters: - input (const pixelroot32::input::InputManager&): The input manager instance

    Returns: - void

    Notes: - Should be called every frame in update() - Checks if button is selected and action button is pressed - Triggers callback if conditions are met

    Example:

    void update(unsigned long deltaTime) override {\n    UIElement::update(deltaTime);\n\n    auto& input = engine.getInputManager();\n    button->handleInput(input);\n}\n

    ","text":""},{"location":"api_reference/ui/ui_button/#void-press","title":"void press()

    Manually triggers the button's action.

    Returns: - void

    Notes: - Calls the button's callback function - Useful for programmatic button presses

    Example:

    button->press();  // Trigger button action\n

    ","text":""},{"location":"api_reference/ui/ui_button/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override

    Updates the button state.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    Notes: - Called automatically by Scene if isEnabled is true - Handles input and updates button state - Override to add custom update logic

    Example:

    void update(unsigned long deltaTime) override {\n    UIButton::update(deltaTime);\n\n    // Custom update logic\n    if (shouldPulse) {\n        // Animate button\n    }\n}\n

    ","text":""},{"location":"api_reference/ui/ui_button/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override

    Renders the button.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): The renderer to use

    Returns: - void

    Notes: - Called automatically by Scene if isVisible is true - Draws background (if enabled) and text - Selected state may change appearance

    Example:

    // Drawing is handled automatically\n// Override only for custom rendering\n

    ","text":""},{"location":"api_reference/ui/ui_button/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIButton.h\"\n#include \"core/Scene.h\"\n\nclass MainMenuScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIButton* startButton;\n    pixelroot32::graphics::ui::UIButton* quitButton;\n\npublic:\n    void init() override {\n        // Start button\n        startButton = new pixelroot32::graphics::ui::UIButton(\n            \"Start Game\",\n            0,  // index\n            64.0f, 50.0f,  // position (centered)\n            120.0f, 30.0f, // size\n            [this]() {\n                // Lambda callback\n                engine.setScene(&gameScene);\n            },\n            pixelroot32::graphics::ui::TextAlignment::CENTER,\n            2\n        );\n        startButton->setStyle(\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Blue,\n            true\n        );\n        addEntity(startButton);\n\n        // Quit button\n        quitButton = new pixelroot32::graphics::ui::UIButton(\n            \"Quit\",\n            1,  // index\n            64.0f, 90.0f,\n            120.0f, 30.0f,\n            [this]() {\n                // Quit game\n                engine.stop();\n            }\n        );\n        quitButton->setStyle(\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Red,\n            true\n        );\n        addEntity(quitButton);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Buttons handle input automatically\n        // Layouts handle navigation automatically\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw background\n        renderer.drawFilledRectangle(0, 0, 128, 128, Color::Black);\n\n        // Draw UI elements (buttons)\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_button/#button-navigation","title":"Button Navigation","text":"

    Buttons can be navigated with D-pad when in layouts:

    // Buttons in a vertical layout\npixelroot32::graphics::ui::UIVerticalLayout* layout = new UIVerticalLayout(64.0f, 50.0f);\nlayout->addChild(startButton);  // Index 0\nlayout->addChild(quitButton);   // Index 1\n\n// D-pad navigation is automatic\n// UP/DOWN moves selection\n// Action button (A) triggers selected button\n
    "},{"location":"api_reference/ui/ui_button/#performance-considerations","title":"Performance Considerations","text":"
    • Input handling: handleInput() is fast; safe to call every frame
    • Rendering: Simple rectangle and text; very efficient
    • Memory: Each button consumes memory (stay within MAX_ENTITIES)
    "},{"location":"api_reference/ui/ui_button/#esp32-considerations","title":"ESP32 Considerations","text":"
    • String storage: Button labels use std::string; consider memory usage
    • Callback functions: Use function pointers or lambdas (both efficient)
    "},{"location":"api_reference/ui/ui_button/#see-also","title":"See Also","text":"
    • UIElement - Base UI element class
    • UILabel - Text label
    • UILayouts - Layout containers
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_checkbox/","title":"UICheckBox","text":"

    A clickable checkbox UI element.

    "},{"location":"api_reference/ui/ui_checkbox/#description","title":"Description","text":"

    UICheckBox is a clickable checkbox that can be toggled between checked and unchecked states. It supports both physical (keyboard/gamepad) and touch input. It can trigger a callback function when its state changes and integrates with UI layouts for automatic navigation.

    "},{"location":"api_reference/ui/ui_checkbox/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UICheckBox : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_checkbox/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    "},{"location":"api_reference/ui/ui_checkbox/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_checkbox/#uicheckboxstring-label-uint8_t-index-float-x-float-y-float-w-float-h-bool-checked-false-function-callback-nullptr-int-fontsize-2","title":"UICheckBox(string label, uint8_t index, float x, float y, float w, float h, bool checked = false, function callback = nullptr, int fontSize = 2)

    Constructs a new UICheckBox.

    Parameters: - label (std::string): Checkbox label text - index (uint8_t): Navigation index (for D-pad navigation in layouts) - x (float): X position - y (float): Y position - w (float): Width - h (float): Height - checked (bool, optional): Initial checked state. Default: false - callback (std::function, optional): Function to call when the state changes. Default: nullptr - fontSize (int, optional): Text size multiplier. Default: 2

    Example:

    #include \"graphics/ui/UICheckBox.h\"\n\nvoid onCheckChanged(bool checked) {\n    if (checked) {\n        // Sound enabled\n    } else {\n        // Sound disabled\n    }\n}\n\n// Create checkbox\npixelroot32::graphics::ui::UICheckBox* soundCheckbox = new pixelroot32::graphics::ui::UICheckBox(\n    \"Enable Sound\",\n    0,  // index\n    64.0f, 64.0f,  // position\n    120.0f, 20.0f, // size\n    true,          // initial state\n    onCheckChanged,\n    1              // font size\n);\n

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_checkbox/#void-setstylecolor-textcol-color-bgcol-bool-drawbg-false","title":"void setStyle(Color textCol, Color bgCol, bool drawBg = false)

    Configures the checkbox's visual style.

    Parameters: - textCol (Color): Color of the text - bgCol (Color): Color of the background - drawBg (bool, optional): Whether to draw the background rectangle. Default: false

    Returns: - void

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#void-setcheckedbool-checked","title":"void setChecked(bool checked)

    Sets the checked state.

    Parameters: - checked (bool): True if checked

    Returns: - void

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#bool-ischecked-const","title":"bool isChecked() const

    Checks if the checkbox is currently checked.

    Returns: - bool: true if checked

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#void-toggle","title":"void toggle()

    Toggles the checkbox state and triggers the callback.

    Returns: - void

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#void-setselectedbool-selected","title":"void setSelected(bool selected)

    Sets the selection state (e.g., focused via D-pad).

    Parameters: - selected (bool): True if selected

    Returns: - void

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#bool-getselected-const","title":"bool getSelected() const

    Checks if the checkbox is currently selected.

    Returns: - bool: true if selected

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#callbacks","title":"Callbacks","text":""},{"location":"api_reference/ui/ui_checkbox/#oncheckchanged","title":"onCheckChanged

    The onCheckChanged callback is a std::function<void(bool)> that is triggered whenever the checkbox state changes via setChecked() or toggle().

    checkbox->onCheckChanged = [](bool isChecked) {\n    Serial.println(isChecked ? \"Checked!\" : \"Unchecked!\");\n};\n
    ","text":""},{"location":"api_reference/ui/ui_checkbox/#navigation-layouts","title":"Navigation & Layouts","text":"

    UICheckBox is designed to work seamlessly with UILayout containers (like UIVerticalLayout).

    • Focusable: Returns true for isFocusable(), allowing it to receive focus in a layout.
    • Input Handling: When selected (focused), it listens for the button index provided in the constructor (typically the 'A' button) to toggle its state.
    • Visual Feedback: When selected, it displays a selection indicator (usually a > character) if no background is drawn, or highlights its text/border.
    "},{"location":"api_reference/ui/ui_element/","title":"UIElement","text":"

    Base class for all user interface elements.

    "},{"location":"api_reference/ui/ui_element/#description","title":"Description","text":"

    UIElement is the base class for all UI components (buttons, labels, panels, etc.). It inherits from Entity to integrate with the scene graph and automatically sets the entity type to UI_ELEMENT and render layer to 2 (UI layer).

    "},{"location":"api_reference/ui/ui_element/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    enum class UIElementType {\n        GENERIC,\n        BUTTON,\n        LABEL,\n        CHECKBOX,\n        LAYOUT\n    };\n\n    class UIElement : public pixelroot32::core::Entity {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_element/#inheritance","title":"Inheritance","text":"
    • Inherits from: pixelroot32::core::Entity
    • Inherited by: UIButton, UICheckBox, UILabel, UIPanel, and all UI layouts
    "},{"location":"api_reference/ui/ui_element/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_element/#uielementfloat-x-float-y-float-w-float-h-uielementtype-type-uielementtypegeneric","title":"UIElement(float x, float y, float w, float h, UIElementType type = UIElementType::GENERIC)","text":"

    Constructs a new UIElement.

    Parameters: - x (float): X position - y (float): Y position - w (float): Width - h (float): Height - type (UIElementType): The type of the element (default: GENERIC)

    Notes: - Entity type is automatically set to UI_ELEMENT - Render layer is automatically set to 2 (UI layer) - Position and size can be modified after construction

    Example:

    class MyUIElement : public pixelroot32::graphics::ui::UIElement {\npublic:\n    MyUIElement(float x, float y) \n        : UIElement(x, y, 100.0f, 50.0f) {}\n\n    void update(unsigned long deltaTime) override {\n        // UI logic\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // UI rendering\n    }\n};\n

    "},{"location":"api_reference/ui/ui_element/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_element/#uielementtype-gettype-const","title":"UIElementType getType() const","text":"

    Returns the type of the UI element.

    Returns: - UIElementType: The type enum value (GENERIC, BUTTON, LABEL, CHECKBOX, or LAYOUT)

    "},{"location":"api_reference/ui/ui_element/#virtual-bool-isfocusable-const","title":"virtual bool isFocusable() const","text":"

    Checks if the element is focusable/selectable. Useful for navigation logic.

    Returns: - bool: true if focusable, false otherwise (default: false)

    "},{"location":"api_reference/ui/ui_element/#void-setpositionfloat-newx-float-newy","title":"void setPosition(float newX, float newY)","text":"

    Sets the position of the element.

    Parameters: - newX (float): New X coordinate - newY (float): New Y coordinate

    Returns: - void

    Notes: - Updates element position immediately - Use for manual positioning or animations

    Example:

    uiElement->setPosition(100.0f, 50.0f);\n

    "},{"location":"api_reference/ui/ui_element/#virtual-void-getpreferredsizefloat-preferredwidth-float-preferredheight-const","title":"virtual void getPreferredSize(float& preferredWidth, float& preferredHeight) const","text":"

    Gets the preferred size of the element. Used by layouts to determine how much space the element needs.

    Parameters: - preferredWidth (float&): Output parameter for preferred width (or -1 if flexible) - preferredHeight (float&): Output parameter for preferred height (or -1 if flexible)

    Returns: - void

    Notes: - Default implementation returns current width/height - Override in derived classes for custom sizing logic - Layouts use this to arrange elements

    Example:

    void getPreferredSize(float& w, float& h) const override {\n    // Custom sizing logic\n    w = static_cast<float>(width);\n    h = static_cast<float>(height);\n}\n

    "},{"location":"api_reference/ui/ui_element/#inherited-from-entity","title":"Inherited from Entity","text":"

    UIElement inherits all properties and methods from Entity:

    • float x, y: Position
    • int width, height: Dimensions
    • bool isVisible: Visibility state
    • bool isEnabled: Enabled state
    • unsigned char renderLayer: Render layer (automatically set to 2)
    • void setVisible(bool v): Set visibility
    • void setEnabled(bool e): Set enabled state
    • virtual void update(unsigned long deltaTime): Update logic
    • virtual void draw(Renderer& renderer): Drawing logic
    "},{"location":"api_reference/ui/ui_element/#textalignment-enum","title":"TextAlignment Enum","text":"

    Text alignment options for UI elements.

    Values: - TextAlignment::LEFT: Left-aligned text - TextAlignment::CENTER: Center-aligned text - TextAlignment::RIGHT: Right-aligned text

    "},{"location":"api_reference/ui/ui_element/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIElement.h\"\n\nclass CustomUIElement : public pixelroot32::graphics::ui::UIElement {\nprivate:\n    pixelroot32::graphics::Color bgColor;\n\npublic:\n    CustomUIElement(float x, float y, float w, float h) \n        : UIElement(x, y, w, h),\n          bgColor(pixelroot32::graphics::Color::Blue) {}\n\n    void update(unsigned long deltaTime) override {\n        // Update logic (if needed)\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        if (isVisible) {\n            // Draw background\n            renderer.drawFilledRectangle(\n                static_cast<int>(x),\n                static_cast<int>(y),\n                width,\n                height,\n                bgColor\n            );\n\n            // Draw border\n            renderer.drawRectangle(\n                static_cast<int>(x),\n                static_cast<int>(y),\n                width,\n                height,\n                pixelroot32::graphics::Color::White\n            );\n        }\n    }\n\n    void getPreferredSize(float& w, float& h) const override {\n        w = static_cast<float>(width);\n        h = static_cast<float>(height);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_element/#performance-considerations","title":"Performance Considerations","text":"
    • Render layer: UI elements are on layer 2, drawn after gameplay
    • Visibility: Use isVisible = false to hide elements efficiently
    • Layout integration: Layouts automatically manage element positioning
    "},{"location":"api_reference/ui/ui_element/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Each UI element consumes memory (stay within MAX_ENTITIES)
    • Object pooling: Reuse UI elements when possible
    • Update frequency: Disable UI elements that don't need to update
    "},{"location":"api_reference/ui/ui_element/#see-also","title":"See Also","text":"
    • Entity - Base entity class
    • UIButton - Clickable button
    • UILabel - Text label
    • UILayout - Layout containers
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_label/","title":"UILabel","text":"

    A simple text label UI element.

    "},{"location":"api_reference/ui/ui_label/#description","title":"Description","text":"

    UILabel displays a string of text on the screen. It auto-calculates its bounds based on text length and font size, making it easy to create dynamic text displays.

    Labels are useful for displaying scores, status messages, menu text, and other UI information.

    "},{"location":"api_reference/ui/ui_label/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UILabel : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_label/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    • Inherited by: Your custom label classes (if needed)
    "},{"location":"api_reference/ui/ui_label/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_label/#uilabelstring-t-float-x-float-y-color-col-uint8_t-sz","title":"UILabel(string t, float x, float y, Color col, uint8_t sz)","text":"

    Constructs a new UILabel.

    Parameters: - t (std::string): Initial text - x (float): X position - y (float): Y position - col (Color): Text color - sz (uint8_t): Text size multiplier

    Example:

    #include \"graphics/ui/UILabel.h\"\n\n// Create label\npixelroot32::graphics::ui::UILabel* scoreLabel = new pixelroot32::graphics::ui::UILabel(\n    \"Score: 0\",\n    10.0f, 10.0f,  // position\n    pixelroot32::graphics::Color::White,\n    1  // size\n);\n\n// Add to scene\nscene->addEntity(scoreLabel);\n

    "},{"location":"api_reference/ui/ui_label/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_label/#void-settextconst-string-t","title":"void setText(const string& t)","text":"

    Updates the label's text. Recalculates dimensions immediately using the active font metrics to ensure precise layout.

    Parameters: - t (const std::string&): New text

    Returns: - void

    Notes: - Automatically recalculates width and height using FontManager::textWidth - Use for dynamic text (scores, timers, etc.) - Text is stored as std::string (consider memory on ESP32)

    Example:

    // Update score label\nchar scoreText[32];\nsnprintf(scoreText, sizeof(scoreText), \"Score: %d\", score);\nscoreLabel->setText(scoreText);\n

    "},{"location":"api_reference/ui/ui_label/#void-setvisiblebool-v","title":"void setVisible(bool v)","text":"

    Sets visibility.

    Parameters: - v (bool): true to show, false to hide

    Returns: - void

    Notes: - Inherited from Entity - Hides label without removing from scene

    Example:

    label->setVisible(false);  // Hide\nlabel->setVisible(true);   // Show\n

    "},{"location":"api_reference/ui/ui_label/#void-centerxint-screenwidth","title":"void centerX(int screenWidth)","text":"

    Centers the label horizontally within a given width (typically the screen width). Recalculates dimensions before positioning to ensure perfect centering.

    Parameters: - screenWidth (int): The width to center within (e.g., engine.getRenderer().getWidth())

    Returns: - void

    Example:

    label->centerX(128); // Center on a 128px screen\n

    "},{"location":"api_reference/ui/ui_label/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the label state.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    Notes: - Called automatically by Scene if isEnabled is true - Default implementation does nothing - Override to add custom update logic (animations, etc.)

    Example:

    void update(unsigned long deltaTime) override {\n    UILabel::update(deltaTime);\n\n    // Custom logic (e.g., update text based on game state)\n    if (scoreChanged) {\n        updateScoreText();\n    }\n}\n

    "},{"location":"api_reference/ui/ui_label/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Renders the label.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): The renderer to use

    Returns: - void

    Notes: - Called automatically by Scene if isVisible is true - Draws text at label position - Uses default font

    Example:

    // Drawing is handled automatically\n// Override only for custom rendering\n

    "},{"location":"api_reference/ui/ui_label/#auto-sizing","title":"Auto-Sizing","text":"

    Labels automatically calculate their size based on text:

    • Width: text.length() * (6 * size) pixels
    • Height: 8 * size pixels

    Example:

    // Label with text \"Hello\" (5 characters), size 1\n// Width: 5 * 6 * 1 = 30 pixels\n// Height: 8 * 1 = 8 pixels\n\nUILabel label(\"Hello\", 10, 10, Color::White, 1);\n// label.width = 30, label.height = 8\n

    "},{"location":"api_reference/ui/ui_label/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UILabel.h\"\n\nclass GameScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UILabel* scoreLabel;\n    pixelroot32::graphics::ui::UILabel* livesLabel;\n    int score = 0;\n    int lives = 3;\n\npublic:\n    void init() override {\n        // Score label\n        scoreLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Score: 0\",\n            10.0f, 10.0f,\n            pixelroot32::graphics::Color::White,\n            1\n        );\n        addEntity(scoreLabel);\n\n        // Lives label\n        livesLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Lives: 3\",\n            10.0f, 25.0f,\n            pixelroot32::graphics::Color::Yellow,\n            1\n        );\n        addEntity(livesLabel);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Update labels\n        char text[32];\n        snprintf(text, sizeof(text), \"Score: %d\", score);\n        scoreLabel->setText(text);\n\n        snprintf(text, sizeof(text), \"Lives: %d\", lives);\n        livesLabel->setText(text);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw game\n        Scene::draw(renderer);\n\n        // Labels are drawn automatically by Scene::draw()\n    }\n};\n
    "},{"location":"api_reference/ui/ui_label/#centered-labels","title":"Centered Labels","text":"
    // Create centered title\npixelroot32::graphics::ui::UILabel* title = new pixelroot32::graphics::ui::UILabel(\n    \"Game Title\",\n    0, 20.0f,  // X will be adjusted\n    pixelroot32::graphics::Color::Yellow,\n    2  // Large text\n);\ntitle->centerX(128);  // Center on screen\naddEntity(title);\n
    "},{"location":"api_reference/ui/ui_label/#performance-considerations","title":"Performance Considerations","text":"
    • Text updates: setText() recalculates size; avoid calling every frame if text doesn't change
    • String storage: Uses std::string; consider memory on ESP32
    • Rendering: Simple text drawing; very efficient
    • Static text: For static text, create once and don't update
    "},{"location":"api_reference/ui/ui_label/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: std::string uses heap memory; use static buffers when possible
    • Text updates: Limit frequency of text updates
    • String length: Keep text short to save memory
    "},{"location":"api_reference/ui/ui_label/#see-also","title":"See Also","text":"
    • UIElement - Base UI element class
    • UIButton - Clickable button
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layout/","title":"UILayout","text":"

    Base class for UI layout containers.

    "},{"location":"api_reference/ui/ui_layout/#description","title":"Description","text":"

    UILayout is the base class for all layout containers. Layouts organize UI elements automatically, handling positioning, spacing, and optional scrolling. Layouts are themselves UI elements that can be added to scenes.

    "},{"location":"api_reference/ui/ui_layout/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UILayout : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layout/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    • Inherited by: UIVerticalLayout, UIHorizontalLayout, UIGridLayout, UIAnchorLayout
    "},{"location":"api_reference/ui/ui_layout/#scrollbehavior-enum","title":"ScrollBehavior Enum","text":"

    Defines how scrolling behaves in layouts.

    Values: - ScrollBehavior::NONE: No scrolling allowed - ScrollBehavior::SCROLL: Scroll freely within bounds - ScrollBehavior::CLAMP: Scroll but clamp to content bounds

    "},{"location":"api_reference/ui/ui_layout/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layout/#virtual-void-addelementuielement-element-0","title":"virtual void addElement(UIElement* element) = 0","text":"

    Adds a UI element to the layout. Must be implemented by derived classes.

    Parameters: - element (UIElement*): Pointer to the element to add

    "},{"location":"api_reference/ui/ui_layout/#virtual-void-removeelementuielement-element-0","title":"virtual void removeElement(UIElement* element) = 0","text":"

    Removes a UI element from the layout. Must be implemented by derived classes.

    Parameters: - element (UIElement*): Pointer to the element to remove

    "},{"location":"api_reference/ui/ui_layout/#virtual-void-updatelayout-0","title":"virtual void updateLayout() = 0","text":"

    Recalculates positions of all elements in the layout. Must be implemented by derived classes.

    Returns: - void

    Notes: - Should be called automatically when elements are added/removed

    "},{"location":"api_reference/ui/ui_layout/#virtual-void-handleinputconst-inputmanager-input-0","title":"virtual void handleInput(const InputManager& input) = 0","text":"

    Handles input for layout navigation (scroll, selection, etc.). Must be implemented by derived classes.

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    "},{"location":"api_reference/ui/ui_layout/#void-setpaddingfloat-p","title":"void setPadding(float p)","text":"

    Sets the padding (internal spacing) of the layout.

    Parameters: - p (float): Padding value in pixels

    Returns: - void

    Notes: - Layout is automatically recalculated

    "},{"location":"api_reference/ui/ui_layout/#float-getpadding-const","title":"float getPadding() const","text":"

    Gets the current padding.

    Returns: - float: Padding value in pixels

    "},{"location":"api_reference/ui/ui_layout/#void-setspacingfloat-s","title":"void setSpacing(float s)","text":"

    Sets the spacing between elements.

    Parameters: - s (float): Spacing value in pixels

    Returns: - void

    Notes: - Layout is automatically recalculated - Default: 4.0 pixels

    "},{"location":"api_reference/ui/ui_layout/#float-getspacing-const","title":"float getSpacing() const","text":"

    Gets the current spacing.

    Returns: - float: Spacing value in pixels

    "},{"location":"api_reference/ui/ui_layout/#size_t-getelementcount-const","title":"size_t getElementCount() const","text":"

    Gets the number of elements in the layout.

    Returns: - size_t: Element count

    "},{"location":"api_reference/ui/ui_layout/#uielement-getelementsize_t-index-const","title":"UIElement* getElement(size_t index) const","text":"

    Gets the element at a specific index.

    Parameters: - index (size_t): Element index

    Returns: - UIElement*: Pointer to the element, or nullptr if index is invalid

    "},{"location":"api_reference/ui/ui_layout/#void-clearelements","title":"void clearElements()","text":"

    Clears all elements from the layout.

    Returns: - void

    Notes: - Elements are not deleted (you must manage their lifetimes) - Layout is automatically recalculated

    "},{"location":"api_reference/ui/ui_layout/#protected-members","title":"Protected Members","text":"
    • std::vector<UIElement*> elements: List of child elements
    • float padding: Internal padding
    • float spacing: Spacing between elements (default: 4.0)
    • float scrollOffset: Current scroll offset
    • bool enableScroll: Whether scrolling is enabled
    • ScrollBehavior scrollBehavior: Scroll behavior mode
    "},{"location":"api_reference/ui/ui_layout/#see-also","title":"See Also","text":"
    • UIElement - Base UI element class
    • UIVerticalLayout - Vertical layout
    • UIHorizontalLayout - Horizontal layout
    • UIGridLayout - Grid layout
    • UIAnchorLayout - Anchor layout
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/","title":"UIAnchorLayout","text":"

    Layout that positions elements at fixed anchor points on the screen.

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#description","title":"Description","text":"

    UIAnchorLayout positions UI elements at fixed anchor points (corners, center, edges) without reflow. Very efficient for HUDs, debug UI, and fixed-position elements. Positions are calculated once or when screen size changes.

    This layout is ideal for HUD elements like score, lives, health bars, and other fixed-position UI.

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIAnchorLayout : public UILayout {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#inheritance","title":"Inheritance","text":"
    • Inherits from: UILayout
    • Inherited by: Your custom anchor layouts (if needed)
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#anchor-enum","title":"Anchor Enum","text":"

    Defines anchor points for positioning UI elements.

    Values: - Anchor::TOP_LEFT: Top-left corner - Anchor::TOP_RIGHT: Top-right corner - Anchor::BOTTOM_LEFT: Bottom-left corner - Anchor::BOTTOM_RIGHT: Bottom-right corner - Anchor::CENTER: Center of screen - Anchor::TOP_CENTER: Top center - Anchor::BOTTOM_CENTER: Bottom center - Anchor::LEFT_CENTER: Left center - Anchor::RIGHT_CENTER: Right center

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/anchor_layout/#uianchorlayoutfloat-x-float-y-float-w-float-h","title":"UIAnchorLayout(float x, float y, float w, float h)","text":"

    Constructs a new UIAnchorLayout.

    Parameters: - x (float): X position of the layout container (usually 0) - y (float): Y position of the layout container (usually 0) - w (float): Width of the layout container (usually screen width) - h (float): Height of the layout container (usually screen height)

    Example:

    #include \"graphics/ui/UIAnchorLayout.h\"\n\n// Create full-screen anchor layout for HUD\nauto& renderer = engine.getRenderer();\npixelroot32::graphics::ui::UIAnchorLayout* hud = \n    new pixelroot32::graphics::ui::UIAnchorLayout(\n        0.0f, 0.0f,\n        static_cast<float>(renderer.getWidth()),\n        static_cast<float>(renderer.getHeight())\n    );\nhud->setScreenSize(\n    static_cast<float>(renderer.getWidth()),\n    static_cast<float>(renderer.getHeight())\n);\n

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-addelementuielement-element-anchor-anchor","title":"void addElement(UIElement* element, Anchor anchor)","text":"

    Adds a UI element with a specific anchor point.

    Parameters: - element (UIElement*): Pointer to the element to add - anchor (Anchor): Anchor point for positioning

    Returns: - void

    Example:

    // Score label at top-right\nhud->addElement(scoreLabel, pixelroot32::graphics::ui::Anchor::TOP_RIGHT);\n\n// Lives label at top-left\nhud->addElement(livesLabel, pixelroot32::graphics::ui::Anchor::TOP_LEFT);\n

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-addelementuielement-element","title":"void addElement(UIElement* element)","text":"

    Adds a UI element to the layout (defaults to TOP_LEFT anchor).

    Parameters: - element (UIElement*): Pointer to the element to add

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-removeelementuielement-element","title":"void removeElement(UIElement* element)","text":"

    Removes a UI element from the layout.

    Parameters: - element (UIElement*): Pointer to the element to remove

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-updatelayout","title":"void updateLayout()","text":"

    Recalculates positions of all elements based on anchors.

    Returns: - void

    Notes: - Called automatically when screen size changes - Can be called manually if needed

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-handleinputconst-inputmanager-input","title":"void handleInput(const InputManager& input)","text":"

    Handles input (no-op for anchor layout, elements handle their own input).

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    Returns: - void

    Notes: - Anchor layout doesn't handle navigation - Elements handle their own input

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the layout and child elements.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws all elements.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-setscreensizefloat-screenwidth-float-screenheight","title":"void setScreenSize(float screenWidth, float screenHeight)","text":"

    Sets the screen size for anchor calculations.

    Parameters: - screenWidth (float): Screen width in pixels - screenHeight (float): Screen height in pixels

    Returns: - void

    Notes: - Used to calculate anchor positions - Should match actual display size - Layout is automatically recalculated

    Example:

    auto& renderer = engine.getRenderer();\nhud->setScreenSize(\n    static_cast<float>(renderer.getWidth()),\n    static_cast<float>(renderer.getHeight())\n);\n

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#float-getscreenwidth-const","title":"float getScreenWidth() const","text":"

    Gets the screen width.

    Returns: - float: Screen width in pixels

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#float-getscreenheight-const","title":"float getScreenHeight() const","text":"

    Gets the screen height.

    Returns: - float: Screen height in pixels

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIAnchorLayout.h\"\n\nclass GameScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIAnchorLayout* hud;\n    pixelroot32::graphics::ui::UILabel* scoreLabel;\n    pixelroot32::graphics::ui::UILabel* livesLabel;\n\npublic:\n    void init() override {\n        auto& renderer = engine.getRenderer();\n\n        // Create HUD layout\n        hud = new pixelroot32::graphics::ui::UIAnchorLayout(\n            0.0f, 0.0f,\n            static_cast<float>(renderer.getWidth()),\n            static_cast<float>(renderer.getHeight())\n        );\n        hud->setScreenSize(\n            static_cast<float>(renderer.getWidth()),\n            static_cast<float>(renderer.getHeight())\n        );\n\n        // Score label (top-right)\n        scoreLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Score: 0\",\n            0, 0,\n            Color::White,\n            1\n        );\n        hud->addElement(scoreLabel, \n                        pixelroot32::graphics::ui::Anchor::TOP_RIGHT);\n\n        // Lives label (top-left)\n        livesLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Lives: 3\",\n            0, 0,\n            Color::Yellow,\n            1\n        );\n        hud->addElement(livesLabel, \n                        pixelroot32::graphics::ui::Anchor::TOP_LEFT);\n\n        addEntity(hud);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Update labels\n        char text[32];\n        snprintf(text, sizeof(text), \"Score: %d\", score);\n        scoreLabel->setText(text);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw game\n        Scene::draw(renderer);\n\n        // HUD is drawn automatically (on layer 2)\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#anchor-positioning","title":"Anchor Positioning","text":"

    Elements are positioned based on their anchor:

    • TOP_LEFT: Element's top-left at screen top-left
    • TOP_RIGHT: Element's top-right at screen top-right
    • BOTTOM_LEFT: Element's bottom-left at screen bottom-left
    • BOTTOM_RIGHT: Element's bottom-right at screen bottom-right
    • CENTER: Element centered on screen
    • TOP_CENTER: Element centered horizontally, top-aligned
    • BOTTOM_CENTER: Element centered horizontally, bottom-aligned
    • LEFT_CENTER: Element centered vertically, left-aligned
    • RIGHT_CENTER: Element centered vertically, right-aligned
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#performance-considerations","title":"Performance Considerations","text":"
    • No reflow: Very efficient (positions calculated once)
    • Fixed positions: Ideal for HUD elements
    • Viewport independent: Elements stay in fixed screen positions
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Very efficient (no complex calculations)
    • Update frequency: Positions only recalculate when screen size changes
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#see-also","title":"See Also","text":"
    • UILayout - Base layout class
    • UILabel - Labels for HUD
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/grid_layout/","title":"UIGridLayout","text":"

    Grid layout container for organizing elements in a matrix.

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#description","title":"Description","text":"

    UIGridLayout organizes UI elements in a fixed grid of rows and columns. It supports navigation in 4 directions (UP/DOWN/LEFT/RIGHT) and automatic positioning based on grid coordinates.

    This layout is ideal for inventories, level selection screens, galleries, and any grid-based UI arrangement.

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIGridLayout : public UILayout {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#inheritance","title":"Inheritance","text":"
    • Inherits from: UILayout
    • Inherited by: Your custom grid layouts (if needed)
    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/grid_layout/#uigridlayoutfloat-x-float-y-float-w-float-h","title":"UIGridLayout(float x, float y, float w, float h)","text":"

    Constructs a new UIGridLayout.

    Parameters: - x (float): X position of the layout container - y (float): Y position of the layout container - w (float): Width of the layout container - h (float): Height of the layout container

    Example:

    #include \"graphics/ui/UIGridLayout.h\"\n\n// Create 4x4 grid for inventory\npixelroot32::graphics::ui::UIGridLayout* inventory = \n    new pixelroot32::graphics::ui::UIGridLayout(\n        10.0f, 10.0f,  // position\n        108.0f, 108.0f // size\n    );\ninventory->setColumns(4);\n

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-addelementuielement-element","title":"void addElement(UIElement* element)","text":"

    Adds a UI element to the layout.

    Parameters: - element (UIElement*): Pointer to the element to add

    Returns: - void

    Notes: - Elements are arranged in grid order (left to right, top to bottom) - Layout is automatically recalculated - Cell size is calculated based on columns and layout size

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-removeelementuielement-element","title":"void removeElement(UIElement* element)","text":"

    Removes a UI element from the layout.

    Parameters: - element (UIElement*): Pointer to the element to remove

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-updatelayout","title":"void updateLayout()","text":"

    Recalculates positions of all elements.

    Returns: - void

    Notes: - Recalculates cell dimensions - Recalculates row count - Repositions all elements

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-handleinputconst-inputmanager-input","title":"void handleInput(const InputManager& input)","text":"

    Handles input for navigation and selection.

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    Returns: - void

    Notes: - Handles UP/DOWN/LEFT/RIGHT navigation - Manages selection state - Wraps around grid edges (if configured)

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the layout and child elements.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws the layout and its visible elements.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-setcolumnsuint8_t-cols","title":"void setColumns(uint8_t cols)","text":"

    Sets the number of columns in the grid.

    Parameters: - cols (uint8_t): Number of columns (must be > 0)

    Returns: - void

    Notes: - Layout is automatically recalculated - Row count is calculated based on element count and columns

    Example:

    inventory->setColumns(4);  // 4 columns\n

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#uint8_t-getcolumns-const","title":"uint8_t getColumns() const","text":"

    Gets the number of columns.

    Returns: - uint8_t: Number of columns

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#uint8_t-getrows-const","title":"uint8_t getRows() const","text":"

    Gets the number of rows (calculated).

    Returns: - uint8_t: Number of rows

    Notes: - Calculated as ceil(elementCount / columns)

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#int-getselectedindex-const","title":"int getSelectedIndex() const","text":"

    Gets the currently selected element index.

    Returns: - int: Selected index, or -1 if none selected

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-setselectedindexint-index","title":"void setSelectedIndex(int index)","text":"

    Sets the selected element index.

    Parameters: - index (int): Index to select (-1 to deselect)

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#uielement-getselectedelement-const","title":"UIElement* getSelectedElement() const","text":"

    Gets the selected element.

    Returns: - UIElement*: Pointer to selected element, or nullptr if none selected

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-setnavigationbuttonsuint8_t-upbutton-uint8_t-downbutton-uint8_t-leftbutton-uint8_t-rightbutton","title":"void setNavigationButtons(uint8_t upButton, uint8_t downButton, uint8_t leftButton, uint8_t rightButton)","text":"

    Sets the navigation button indices.

    Parameters: - upButton (uint8_t): Button index for UP navigation - downButton (uint8_t): Button index for DOWN navigation - leftButton (uint8_t): Button index for LEFT navigation - rightButton (uint8_t): Button index for RIGHT navigation

    Returns: - void

    Notes: - Default: UP=0, DOWN=1, LEFT=2, RIGHT=3 - Change if your input mapping differs

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-setbuttonstylecolor-selectedtextcol-color-selectedbgcol-color-unselectedtextcol-color-unselectedbgcol","title":"void setButtonStyle(Color selectedTextCol, Color selectedBgCol, Color unselectedTextCol, Color unselectedBgCol)","text":"

    Sets the style colors for selected and unselected buttons.

    Parameters: - selectedTextCol (Color): Text color when selected - selectedBgCol (Color): Background color when selected - unselectedTextCol (Color): Text color when not selected - unselectedBgCol (Color): Background color when not selected

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIGridLayout.h\"\n\nclass InventoryScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIGridLayout* inventory;\n\npublic:\n    void init() override {\n        // Create 4x4 inventory grid\n        inventory = new pixelroot32::graphics::ui::UIGridLayout(\n            10.0f, 10.0f,\n            108.0f, 108.0f\n        );\n        inventory->setColumns(4);\n        inventory->setSpacing(2.0f);\n        inventory->setPadding(4.0f);\n\n        // Add inventory slots\n        for (int i = 0; i < 16; i++) {\n            auto* slot = createInventorySlot(i);\n            inventory->addElement(slot);\n        }\n\n        addEntity(inventory);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#see-also","title":"See Also","text":"
    • UILayout - Base layout class (abstract)
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/","title":"UIHorizontalLayout","text":"

    Horizontal layout container with scroll support.

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#description","title":"Description","text":"

    UIHorizontalLayout organizes UI elements horizontally, one next to another. It supports scrolling when content exceeds the visible viewport and handles keyboard/D-pad navigation automatically.

    This layout is ideal for toolbars, tab bars, horizontal menus, and any horizontal arrangement of UI elements.

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIHorizontalLayout : public UILayout {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#inheritance","title":"Inheritance","text":"
    • Inherits from: UILayout
    • Inherited by: Your custom horizontal layouts (if needed)
    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#uihorizontallayoutfloat-x-float-y-float-w-float-h","title":"UIHorizontalLayout(float x, float y, float w, float h)","text":"

    Constructs a new UIHorizontalLayout.

    Parameters: - x (float): X position of the layout container - y (float): Y position of the layout container - w (float): Width of the layout container (viewport width) - h (float): Height of the layout container

    Example:

    #include \"graphics/ui/UIHorizontalLayout.h\"\n\n// Create horizontal layout for toolbar\npixelroot32::graphics::ui::UIHorizontalLayout* toolbar = \n    new pixelroot32::graphics::ui::UIHorizontalLayout(\n        0.0f, 0.0f,   // position (top of screen)\n        128.0f, 20.0f // size\n    );\n

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-addelementuielement-element","title":"void addElement(UIElement* element)","text":"

    Adds a UI element to the layout.

    Parameters: - element (UIElement*): Pointer to the element to add

    Returns: - void

    Notes: - Elements are arranged horizontally, left to right - Layout is automatically recalculated - Elements are positioned based on spacing and padding

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-removeelementuielement-element","title":"void removeElement(UIElement* element)","text":"

    Removes a UI element from the layout.

    Parameters: - element (UIElement*): Pointer to the element to remove

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-updatelayout","title":"void updateLayout()","text":"

    Recalculates positions of all elements.

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-handleinputconst-inputmanager-input","title":"void handleInput(const InputManager& input)","text":"

    Handles input for navigation and scrolling.

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    Returns: - void

    Notes: - Handles LEFT/RIGHT navigation - Manages selection state - Handles scrolling if enabled

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the layout (handles smooth scrolling).

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws the layout and its visible elements.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setscrollenabledbool-enable","title":"void setScrollEnabled(bool enable)","text":"

    Enables or disables scrolling.

    Parameters: - enable (bool): true to enable scrolling

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setviewportwidthfloat-w","title":"void setViewportWidth(float w)","text":"

    Sets the viewport width (visible area).

    Parameters: - w (float): Viewport width in pixels

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#float-getscrolloffset-const","title":"float getScrollOffset() const","text":"

    Gets the current scroll offset.

    Returns: - float: Scroll offset in pixels

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setscrolloffsetfloat-offset","title":"void setScrollOffset(float offset)","text":"

    Sets the scroll offset directly.

    Parameters: - offset (float): Scroll offset in pixels

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#float-getcontentwidth-const","title":"float getContentWidth() const","text":"

    Gets the total content width.

    Returns: - float: Content width in pixels

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#int-getselectedindex-const","title":"int getSelectedIndex() const","text":"

    Gets the currently selected element index.

    Returns: - int: Selected index, or -1 if none selected

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setselectedindexint-index","title":"void setSelectedIndex(int index)","text":"

    Sets the selected element index.

    Parameters: - index (int): Index to select (-1 to deselect)

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#uielement-getselectedelement-const","title":"UIElement* getSelectedElement() const","text":"

    Gets the selected element.

    Returns: - UIElement*: Pointer to selected element, or nullptr if none selected

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setscrollspeedfloat-speed","title":"void setScrollSpeed(float speed)","text":"

    Sets the scroll speed for smooth scrolling.

    Parameters: - speed (float): Pixels per millisecond

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setnavigationbuttonsuint8_t-leftbutton-uint8_t-rightbutton","title":"void setNavigationButtons(uint8_t leftButton, uint8_t rightButton)","text":"

    Sets the navigation button indices.

    Parameters: - leftButton (uint8_t): Button index for LEFT navigation - rightButton (uint8_t): Button index for RIGHT navigation

    Returns: - void

    Notes: - Default: LEFT = 2, RIGHT = 3 - Change if your input mapping differs

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setbuttonstylecolor-selectedtextcol-color-selectedbgcol-color-unselectedtextcol-color-unselectedbgcol","title":"void setButtonStyle(Color selectedTextCol, Color selectedBgCol, Color unselectedTextCol, Color unselectedBgCol)","text":"

    Sets the style colors for selected and unselected buttons.

    Parameters: - selectedTextCol (Color): Text color when selected - selectedBgCol (Color): Background color when selected - unselectedTextCol (Color): Text color when not selected - unselectedBgCol (Color): Background color when not selected

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIHorizontalLayout.h\"\n\nclass ToolbarScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIHorizontalLayout* toolbar;\n\npublic:\n    void init() override {\n        // Create horizontal toolbar\n        toolbar = new pixelroot32::graphics::ui::UIHorizontalLayout(\n            0.0f, 0.0f,    // Top of screen\n            128.0f, 20.0f  // Full width, 20px tall\n        );\n        toolbar->setSpacing(4.0f);\n        toolbar->setPadding(2.0f);\n\n        // Add toolbar buttons\n        toolbar->addElement(newButton(\"File\", ...));\n        toolbar->addElement(newButton(\"Edit\", ...));\n        toolbar->addElement(newButton(\"View\", ...));\n\n        addEntity(toolbar);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#see-also","title":"See Also","text":"
    • UILayout - Base layout class (abstract)
    • UIVerticalLayout - Vertical layout
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/padding_container/","title":"UIPaddingContainer","text":"

    Container that wraps a single UI element and applies padding.

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#description","title":"Description","text":"

    UIPaddingContainer adds padding/margin around a single child element without organizing multiple elements. Useful for adding spacing to individual elements or nesting layouts with custom padding.

    This container is simpler than UIPanel (no background/border) and focuses only on spacing.

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIPaddingContainer : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    • Inherited by: Your custom padding containers (if needed)
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/padding_container/#uipaddingcontainerfloat-x-float-y-float-w-float-h","title":"UIPaddingContainer(float x, float y, float w, float h)","text":"

    Constructs a new UIPaddingContainer.

    Parameters: - x (float): X position of the container - y (float): Y position of the container - w (float): Width of the container - h (float): Height of the container

    Example:

    #include \"graphics/ui/UIPaddingContainer.h\"\n\n// Create padding container\npixelroot32::graphics::ui::UIPaddingContainer* padded = \n    new pixelroot32::graphics::ui::UIPaddingContainer(\n        10.0f, 10.0f,\n        108.0f, 108.0f\n    );\npadded->setPadding(8.0f);  // 8px padding on all sides\n

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/padding_container/#void-setchilduielement-element","title":"void setChild(UIElement* element)","text":"

    Sets the child element.

    Parameters: - element (UIElement*): Pointer to the UI element to wrap

    Returns: - void

    Notes: - Child element is positioned with padding applied - Can wrap any UI element (button, label, layout, etc.)

    Example:

    padded->setChild(button);\n

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#uielement-getchild-const","title":"UIElement* getChild() const","text":"

    Gets the child element.

    Returns: - UIElement*: Pointer to the child element, or nullptr if none set

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#void-setpaddingfloat-p","title":"void setPadding(float p)","text":"

    Sets uniform padding on all sides.

    Parameters: - p (float): Padding value in pixels

    Returns: - void

    Notes: - Applies same padding to all sides - Child position is automatically updated

    Example:

    padded->setPadding(10.0f);  // 10px padding all around\n

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#void-setpaddingfloat-left-float-right-float-top-float-bottom","title":"void setPadding(float left, float right, float top, float bottom)","text":"

    Sets asymmetric padding.

    Parameters: - left (float): Left padding in pixels - right (float): Right padding in pixels - top (float): Top padding in pixels - bottom (float): Bottom padding in pixels

    Returns: - void

    Example:

    padded->setPadding(10.0f, 5.0f, 8.0f, 12.0f);  // Different padding per side\n

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#float-getpaddingleft-const","title":"float getPaddingLeft() const","text":"

    Gets the left padding.

    Returns: - float: Left padding in pixels

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#float-getpaddingright-const","title":"float getPaddingRight() const","text":"

    Gets the right padding.

    Returns: - float: Right padding in pixels

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#float-getpaddingtop-const","title":"float getPaddingTop() const","text":"

    Gets the top padding.

    Returns: - float: Top padding in pixels

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#float-getpaddingbottom-const","title":"float getPaddingBottom() const","text":"

    Gets the bottom padding.

    Returns: - float: Bottom padding in pixels

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#void-setpositionfloat-newx-float-newy","title":"void setPosition(float newX, float newY)","text":"

    Sets the position of the container. Also updates the child element's position.

    Parameters: - newX (float): New X coordinate - newY (float): New Y coordinate

    Returns: - void

    Notes: - Child element position is updated automatically with padding applied

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the container and child element.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    Notes: - Updates child element if set - Called automatically by Scene

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws the child element.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    Notes: - Only draws child element (no background/border) - Child is drawn at padded position

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIPaddingContainer.h\"\n\nclass MenuScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Create button\n        auto* button = new UIButton(\"Start\", 0, 0, 0, 100.0f, 30.0f, \n                                    [this]() { startGame(); });\n\n        // Wrap button with padding\n        auto* paddedButton = new pixelroot32::graphics::ui::UIPaddingContainer(\n            64.0f, 50.0f,  // position\n            120.0f, 50.0f  // size (button + padding)\n        );\n        paddedButton->setPadding(10.0f);  // 10px padding\n        paddedButton->setChild(button);\n\n        addEntity(paddedButton);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#nesting-with-layouts","title":"Nesting with Layouts","text":"
    // Create layout\nauto* layout = new UIVerticalLayout(10, 10, 108, 108);\n\n// Wrap layout with padding\nauto* paddedLayout = new UIPaddingContainer(0, 0, 128, 128);\npaddedLayout->setPadding(10.0f, 10.0f, 20.0f, 20.0f);  // Asymmetric\npaddedLayout->setChild(layout);\n
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#performance-considerations","title":"Performance Considerations","text":"
    • Rendering: Very efficient (just draws child)
    • Position calculation: Fast (simple addition)
    • Memory: Minimal overhead
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Very lightweight
    • Update frequency: Position only recalculates when padding/position changes
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#see-also","title":"See Also","text":"
    • UIElement - Base UI element class
    • UIPanel - Panel with background and border
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/panel/","title":"UIPanel","text":"

    Visual container that draws a background and border around a child element.

    "},{"location":"api_reference/ui/ui_layouts/panel/#description","title":"Description","text":"

    UIPanel provides a retro-style window/panel appearance with a background color and border. Typically contains a UILayout or other UI elements. Useful for dialogs, menus, and information panels.

    The panel wraps a single child element and draws a background rectangle and border around it.

    "},{"location":"api_reference/ui/ui_layouts/panel/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIPanel : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/panel/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    • Inherited by: Your custom panel classes (if needed)
    "},{"location":"api_reference/ui/ui_layouts/panel/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/panel/#uipanelfloat-x-float-y-float-w-float-h","title":"UIPanel(float x, float y, float w, float h)","text":"

    Constructs a new UIPanel.

    Parameters: - x (float): X position of the panel - y (float): Y position of the panel - w (float): Width of the panel - h (float): Height of the panel

    Example:

    #include \"graphics/ui/UIPanel.h\"\n\n// Create dialog panel\npixelroot32::graphics::ui::UIPanel* dialog = \n    new pixelroot32::graphics::ui::UIPanel(\n        20.0f, 30.0f,  // position\n        88.0f, 68.0f   // size\n    );\n

    "},{"location":"api_reference/ui/ui_layouts/panel/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/panel/#void-setchilduielement-element","title":"void setChild(UIElement* element)","text":"

    Sets the child element.

    Parameters: - element (UIElement*): Pointer to the UI element to wrap (typically a UILayout)

    Returns: - void

    Notes: - Child element is positioned inside the panel (with padding) - Typically a layout (VerticalLayout, etc.)

    Example:

    // Create panel\nauto* panel = new UIPanel(20, 30, 88, 68);\n\n// Create layout for panel content\nauto* layout = new UIVerticalLayout(0, 0, 80, 60);\nlayout->addElement(button1);\nlayout->addElement(button2);\n\n// Set layout as panel child\npanel->setChild(layout);\n

    "},{"location":"api_reference/ui/ui_layouts/panel/#uielement-getchild-const","title":"UIElement* getChild() const","text":"

    Gets the child element.

    Returns: - UIElement*: Pointer to the child element, or nullptr if none set

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-setbackgroundcolorcolor-color","title":"void setBackgroundColor(Color color)","text":"

    Sets the background color.

    Parameters: - color (Color): Background color

    Returns: - void

    Example:

    panel->setBackgroundColor(Color::Blue);\n

    "},{"location":"api_reference/ui/ui_layouts/panel/#color-getbackgroundcolor-const","title":"Color getBackgroundColor() const","text":"

    Gets the background color.

    Returns: - Color: Background color

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-setbordercolorcolor-color","title":"void setBorderColor(Color color)","text":"

    Sets the border color.

    Parameters: - color (Color): Border color

    Returns: - void

    Example:

    panel->setBorderColor(Color::White);\n

    "},{"location":"api_reference/ui/ui_layouts/panel/#color-getbordercolor-const","title":"Color getBorderColor() const","text":"

    Gets the border color.

    Returns: - Color: Border color

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-setborderwidthuint8_t-width","title":"void setBorderWidth(uint8_t width)","text":"

    Sets the border width.

    Parameters: - width (uint8_t): Border width in pixels

    Returns: - void

    Notes: - Default: 1 pixel - Higher values = thicker border

    Example:

    panel->setBorderWidth(2);  // 2 pixel border\n

    "},{"location":"api_reference/ui/ui_layouts/panel/#uint8_t-getborderwidth-const","title":"uint8_t getBorderWidth() const","text":"

    Gets the border width.

    Returns: - uint8_t: Border width in pixels

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-setpositionfloat-newx-float-newy","title":"void setPosition(float newX, float newY)","text":"

    Sets the position of the panel. Also updates the child element's position.

    Parameters: - newX (float): New X coordinate - newY (float): New Y coordinate

    Returns: - void

    Notes: - Child element position is updated automatically

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the panel and child element.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    Notes: - Updates child element if set - Called automatically by Scene

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws the panel (background, border) and child element.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    Notes: - Draws background rectangle - Draws border rectangle - Draws child element if set

    "},{"location":"api_reference/ui/ui_layouts/panel/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIPanel.h\"\n#include \"graphics/ui/UIVerticalLayout.h\"\n\nclass DialogScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIPanel* dialog;\n\npublic:\n    void init() override {\n        // Create dialog panel\n        dialog = new pixelroot32::graphics::ui::UIPanel(\n            20.0f, 30.0f,  // position\n            88.0f, 68.0f   // size\n        );\n        dialog->setBackgroundColor(Color::Navy);\n        dialog->setBorderColor(Color::White);\n        dialog->setBorderWidth(2);\n\n        // Create layout for dialog content\n        auto* layout = new pixelroot32::graphics::ui::UIVerticalLayout(\n            4.0f, 4.0f,  // Position inside panel\n            80.0f, 60.0f // Size inside panel\n        );\n        layout->setSpacing(8.0f);\n\n        // Add buttons\n        auto* okButton = new UIButton(\"OK\", 0, 0, 0, 70.0f, 20.0f, \n                                     [this]() { closeDialog(); });\n        auto* cancelButton = new UIButton(\"Cancel\", 1, 0, 0, 70.0f, 20.0f,\n                                         [this]() { closeDialog(); });\n\n        layout->addElement(okButton);\n        layout->addElement(cancelButton);\n\n        // Set layout as panel child\n        dialog->setChild(layout);\n\n        addEntity(dialog);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/panel/#performance-considerations","title":"Performance Considerations","text":"
    • Rendering: Simple rectangles; very efficient
    • Child updates: Child element updates are fast
    • Memory: Small overhead (just colors and border width)
    "},{"location":"api_reference/ui/ui_layouts/panel/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Panel is lightweight
    • Rendering: Two rectangles (background + border); minimal overhead
    "},{"location":"api_reference/ui/ui_layouts/panel/#see-also","title":"See Also","text":"
    • UIElement - Base UI element class
    • UILayouts - Layout containers to use inside panels
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/","title":"UIVerticalLayout","text":"

    Vertical layout container with scroll support.

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#description","title":"Description","text":"

    UIVerticalLayout organizes UI elements vertically, one below another. It supports scrolling when content exceeds the visible viewport and handles keyboard/D-pad navigation automatically.

    This layout is ideal for menus, lists, and any vertical arrangement of UI elements.

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIVerticalLayout : public UILayout {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#inheritance","title":"Inheritance","text":"
    • Inherits from: UILayout
    • Inherited by: Your custom vertical layouts (if needed)
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/vertical_layout/#uiverticallayoutfloat-x-float-y-float-w-float-h","title":"UIVerticalLayout(float x, float y, float w, float h)","text":"

    Constructs a new UIVerticalLayout.

    Parameters: - x (float): X position of the layout container - y (float): Y position of the layout container - w (float): Width of the layout container - h (float): Height of the layout container (viewport height)

    Example:

    #include \"graphics/ui/UIVerticalLayout.h\"\n\n// Create vertical layout for menu\npixelroot32::graphics::ui::UIVerticalLayout* menuLayout = \n    new pixelroot32::graphics::ui::UIVerticalLayout(\n        20.0f, 20.0f,  // position\n        88.0f, 88.0f   // size (viewport)\n    );\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-addelementuielement-element","title":"void addElement(UIElement* element)","text":"

    Adds a UI element to the layout.

    Parameters: - element (UIElement*): Pointer to the element to add

    Returns: - void

    Notes: - Elements are arranged vertically, one below another - Layout is automatically recalculated - Elements are positioned based on spacing and padding

    Example:

    menuLayout->addElement(startButton);\nmenuLayout->addElement(quitButton);\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-removeelementuielement-element","title":"void removeElement(UIElement* element)","text":"

    Removes a UI element from the layout.

    Parameters: - element (UIElement*): Pointer to the element to remove

    Returns: - void

    Notes: - Layout is automatically recalculated - Element is not deleted (you must manage its lifetime)

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-updatelayout","title":"void updateLayout()","text":"

    Recalculates positions of all elements.

    Returns: - void

    Notes: - Called automatically when elements are added/removed - Can be called manually if needed - Recalculates all element positions and content height

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-handleinputconst-inputmanager-input","title":"void handleInput(const InputManager& input)","text":"

    Handles input for navigation and scrolling.

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    Returns: - void

    Notes: - Handles UP/DOWN navigation - Manages selection state - Handles scrolling if enabled - Should be called every frame in update()

    Example:

    void update(unsigned long deltaTime) override {\n    UIVerticalLayout::update(deltaTime);\n\n    auto& input = engine.getInputManager();\n    menuLayout->handleInput(input);\n}\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the layout (handles smooth scrolling).

    Parameters: - deltaTime (unsigned long): Time elapsed since last frame in milliseconds

    Returns: - void

    Notes: - Called automatically by Scene if isEnabled is true - Updates smooth scrolling animation - Updates child elements

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws the layout and its visible elements.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    Notes: - Called automatically by Scene if isVisible is true - Only draws visible elements (viewport culling) - Draws elements in order

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setscrollenabledbool-enable","title":"void setScrollEnabled(bool enable)","text":"

    Enables or disables scrolling.

    Parameters: - enable (bool): true to enable scrolling

    Returns: - void

    Notes: - When disabled, scroll offset is reset to 0 - Scrolling is useful when content exceeds viewport height

    Example:

    menuLayout->setScrollEnabled(true);  // Enable scrolling\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-enablescrollbool-enable","title":"void enableScroll(bool enable)","text":"

    Alias for setScrollEnabled().

    Parameters: - enable (bool): true to enable scrolling

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setviewportheightfloat-h","title":"void setViewportHeight(float h)","text":"

    Sets the viewport height (visible area).

    Parameters: - h (float): Viewport height in pixels

    Returns: - void

    Notes: - Layout is automatically recalculated - Use to adjust visible area

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#float-getscrolloffset-const","title":"float getScrollOffset() const","text":"

    Gets the current scroll offset.

    Returns: - float: Scroll offset in pixels

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setscrolloffsetfloat-offset","title":"void setScrollOffset(float offset)","text":"

    Sets the scroll offset directly.

    Parameters: - offset (float): Scroll offset in pixels

    Returns: - void

    Notes: - Offset is clamped to valid range automatically - Use for programmatic scrolling

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#float-getcontentheight-const","title":"float getContentHeight() const","text":"

    Gets the total content height.

    Returns: - float: Content height in pixels

    Notes: - Includes all elements plus spacing and padding - Useful for scroll calculations

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#int-getselectedindex-const","title":"int getSelectedIndex() const","text":"

    Gets the currently selected element index.

    Returns: - int: Selected index, or -1 if none selected

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setselectedindexint-index","title":"void setSelectedIndex(int index)","text":"

    Sets the selected element index.

    Parameters: - index (int): Index to select (-1 to deselect)

    Returns: - void

    Notes: - Selected element is highlighted - Selection is scrolled into view if needed

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#uielement-getselectedelement-const","title":"UIElement* getSelectedElement() const","text":"

    Gets the selected element.

    Returns: - UIElement*: Pointer to selected element, or nullptr if none selected

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setscrollspeedfloat-speed","title":"void setScrollSpeed(float speed)","text":"

    Sets the scroll speed for smooth scrolling.

    Parameters: - speed (float): Pixels per millisecond

    Returns: - void

    Notes: - Default: 0.5 pixels per millisecond - Higher values = faster scrolling

    Example:

    menuLayout->setScrollSpeed(1.0f);  // Faster scrolling\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setnavigationbuttonsuint8_t-upbutton-uint8_t-downbutton","title":"void setNavigationButtons(uint8_t upButton, uint8_t downButton)","text":"

    Sets the navigation button indices.

    Parameters: - upButton (uint8_t): Button index for UP navigation - downButton (uint8_t): Button index for DOWN navigation

    Returns: - void

    Notes: - Default: UP = 0, DOWN = 1 - Change if your input mapping differs

    Example:

    menuLayout->setNavigationButtons(0, 1);  // UP=0, DOWN=1\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setbuttonstylecolor-selectedtextcol-color-selectedbgcol-color-unselectedtextcol-color-unselectedbgcol","title":"void setButtonStyle(Color selectedTextCol, Color selectedBgCol, Color unselectedTextCol, Color unselectedBgCol)","text":"

    Sets the style colors for selected and unselected buttons.

    Parameters: - selectedTextCol (Color): Text color when selected - selectedBgCol (Color): Background color when selected - unselectedTextCol (Color): Text color when not selected - unselectedBgCol (Color): Background color when not selected

    Returns: - void

    Example:

    menuLayout->setButtonStyle(\n    Color::Yellow,  // Selected text\n    Color::Blue,    // Selected background\n    Color::White,   // Unselected text\n    Color::Black    // Unselected background\n);\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIVerticalLayout.h\"\n#include \"graphics/ui/UIButton.h\"\n\nclass MainMenuScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIVerticalLayout* menuLayout;\n\npublic:\n    void init() override {\n        // Create menu layout\n        menuLayout = new pixelroot32::graphics::ui::UIVerticalLayout(\n            20.0f, 20.0f,  // position\n            88.0f, 88.0f   // size\n        );\n        menuLayout->setScrollEnabled(true);\n        menuLayout->setSpacing(8.0f);\n        menuLayout->setPadding(4.0f);\n\n        // Create buttons\n        auto* startButton = new pixelroot32::graphics::ui::UIButton(\n            \"Start\",\n            0, 64.0f, 50.0f, 100.0f, 30.0f,\n            [this]() { engine.setScene(&gameScene); }\n        );\n\n        auto* quitButton = new pixelroot32::graphics::ui::UIButton(\n            \"Quit\",\n            1, 64.0f, 50.0f, 100.0f, 30.0f,\n            [this]() { engine.stop(); }\n        );\n\n        // Add buttons to layout\n        menuLayout->addElement(startButton);\n        menuLayout->addElement(quitButton);\n\n        // Add layout to scene\n        addEntity(menuLayout);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Layout handles input automatically\n        auto& input = engine.getInputManager();\n        menuLayout->handleInput(input);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(0, 0, 128, 128, Color::Black);\n        Scene::draw(renderer);  // Draws layout and buttons\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#navigation","title":"Navigation","text":"

    The layout handles D-pad navigation automatically:

    • UP button: Moves selection up
    • DOWN button: Moves selection down
    • Action button: Triggers selected button's callback
    • Scrolling: Automatically scrolls to keep selected element visible
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#performance-considerations","title":"Performance Considerations","text":"
    • Viewport culling: Only visible elements are drawn
    • Layout recalculation: Fast (simple positioning)
    • Scrolling: Smooth scrolling is efficient
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Element count: Stay within MAX_ENTITIES limit
    • Scrolling: Smooth scrolling uses minimal CPU
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#see-also","title":"See Also","text":"
    • UILayout - Base layout class (abstract)
    • UIButton - Buttons for menus
    • Manual - User Interface
    • API Overview
    "},{"location":"getting_started/fundamental_concepts/","title":"Fundamental Concepts","text":"

    Before you start programming, it's important to understand the basic concepts that form PixelRoot32's architecture. This section explains how the engine works at a conceptual level, without going into code details.

    "},{"location":"getting_started/fundamental_concepts/#engine-architecture","title":"Engine Architecture","text":""},{"location":"getting_started/fundamental_concepts/#engine-the-heart-of-the-engine","title":"Engine: The Heart of the Engine","text":"

    The Engine is the main class that orchestrates the entire system. Think of it as the conductor that coordinates all subsystems:

    • Renderer: Handles drawing everything on screen
    • InputManager: Reads and processes user input (buttons, keyboard)
    • AudioEngine: Generates and plays sounds and music
    • SceneManager: Manages game scenes (menus, levels, etc.)

    The Engine runs the main game loop: an infinite cycle that updates game logic and draws each frame on screen. It also calculates delta time (time elapsed between frames) so the game runs at the same speed regardless of framerate.

    "},{"location":"getting_started/fundamental_concepts/#scene-organizing-your-game","title":"Scene: Organizing Your Game","text":"

    A Scene represents a screen or level in your game. For example: - A scene for the main menu - A scene for each game level - A scene for the game over screen - A scene for the pause menu

    Each scene contains and manages a set of entities (characters, enemies, objects, etc.). The scene is responsible for: - Initializing its entities when loaded - Updating the logic of all its entities each frame - Drawing all its visible entities each frame - Managing collisions between entities that can collide

    The Engine can only have one active scene at a time, but you can easily switch between scenes (for example, go from menu to game, or from game to pause menu).

    "},{"location":"getting_started/fundamental_concepts/#entity-the-fundamental-building-blocks","title":"Entity: The Fundamental Building Blocks","text":"

    An Entity is any object in your game that has: - Position (x, y) in the world - Size (width and height) - Visibility (can be visible or not) - Active state (can be enabled or disabled) - Render layer (in what order it's drawn)

    Entities are the foundation of everything in your game: the player, enemies, projectiles, objects, UI elements\u2014everything is an entity or inherits from Entity.

    Each entity has two main methods: - update(): Called each frame to update the entity's logic (movement, animation, etc.) - draw(): Called each frame to draw the entity on screen

    "},{"location":"getting_started/fundamental_concepts/#actor-entities-that-can-collide","title":"Actor: Entities That Can Collide","text":"

    An Actor is a special entity that can participate in the collision system. In addition to everything an Entity has, an Actor has: - Collision layer: Which group it belongs to (e.g., \"player\", \"enemy\", \"projectile\") - Collision mask: Which other groups it can collide with - Hitbox: The shape used to detect collisions (usually a rectangle)

    For example, a player might be on the \"player\" layer and have a mask that allows it to collide with \"enemies\" and \"obstacles\", but not with \"other players\".

    When two actors collide, the system calls their onCollision() method so they can react (e.g., player loses health, enemy is destroyed, etc.).

    "},{"location":"getting_started/fundamental_concepts/#physicsactor-entities-with-physics","title":"PhysicsActor: Entities with Physics","text":"

    A PhysicsActor is an Actor that also has physical properties: - Velocity (vx, vy): Moves automatically according to its velocity - Gravity: Can fall automatically - Friction: Gradually loses velocity - Restitution: Bounces when it collides (like a ball)

    The PhysicsActor updates automatically each frame, applying physics and moving the entity. It can also detect collisions with world boundaries (the walls of the play area).

    "},{"location":"getting_started/fundamental_concepts/#entity-hierarchy","title":"Entity Hierarchy","text":"

    The relationship between these classes is hierarchical:

    Entity (base)\n  \u2514\u2500\u2500 Actor (can collide)\n       \u2514\u2500\u2500 PhysicsActor (has physics)\n

    This means: - Every Actor is also an Entity - Every PhysicsActor is also an Actor and an Entity - You can use Entity for simple objects that don't need collisions - You can use Actor for objects that need to detect collisions - You can use PhysicsActor for objects that need automatic physics

    "},{"location":"getting_started/fundamental_concepts/#rendering-system","title":"Rendering System","text":""},{"location":"getting_started/fundamental_concepts/#render-layers","title":"Render Layers","text":"

    To control the order in which things are drawn, PixelRoot32 uses render layers:

    • Layer 0 (Background): Backgrounds, tilemaps, background elements
    • Layer 1 (Gameplay): Characters, enemies, projectiles, game objects
    • Layer 2 (UI): Menus, HUD, text, interface elements

    Layers are drawn in order: first 0, then 1, and finally 2. This ensures the background is always behind, gameplay in the middle, and UI always visible in front.

    Each entity has a renderLayer property that indicates which layer it should be drawn on. You can change this property to move entities between layers.

    "},{"location":"getting_started/fundamental_concepts/#rendering-pipeline","title":"Rendering Pipeline","text":"

    The rendering process works like this:

    1. beginFrame(): The screen is cleared (painted black or background color)
    2. Draw entities: All visible entities are traversed, organized by layer
    3. endFrame(): The complete frame is sent to the display

    The Renderer abstracts hardware details, so the same code works on both ESP32 (TFT_eSPI) and PC (SDL2).

    "},{"location":"getting_started/fundamental_concepts/#coordinates-and-space","title":"Coordinates and Space","text":"

    PixelRoot32 uses a standard coordinate system: - Origin (0, 0): Top-left corner - X-axis: Increases to the right - Y-axis: Increases downward

    Coordinates are in pixels. If your display is 240x240, coordinates range from (0, 0) to (239, 239).

    "},{"location":"getting_started/fundamental_concepts/#lifecycle","title":"Lifecycle","text":""},{"location":"getting_started/fundamental_concepts/#initialization","title":"Initialization","text":"

    When your game starts:

    1. Configuration: Configuration objects are created (DisplayConfig, InputConfig, AudioConfig)
    2. Engine: The Engine is created with these configurations
    3. init(): engine.init() is called to initialize all subsystems
    4. Scene: The initial scene is created and configured
    5. setScene(): The scene is assigned to the Engine
    "},{"location":"getting_started/fundamental_concepts/#game-loop","title":"Game Loop","text":"

    Once initialized, the Engine enters the game loop:

    While the game is running:\n  1. Calculate deltaTime (time since last frame)\n  2. Update InputManager (read buttons/keyboard)\n  3. Update AudioEngine (advance sounds and music)\n  4. Update current scene (update all entities)\n  5. Detect collisions in the scene\n  6. Draw the scene (draw all visible entities)\n  7. Repeat\n

    This cycle runs continuously, typically at 30-60 FPS on ESP32, or faster on PC.

    "},{"location":"getting_started/fundamental_concepts/#update","title":"Update","text":"

    Each frame, all enabled entities receive a call to their update(deltaTime) method. This is where: - Entities move - Animations update - Game logic is processed - User input is read - Sound effects are played

    The deltaTime is passed in milliseconds and represents how much time has passed since the last frame. This allows movement to be framerate-independent.

    "},{"location":"getting_started/fundamental_concepts/#rendering-draw","title":"Rendering (Draw)","text":"

    After updating, all visible entities receive a call to their draw(renderer) method. This is where: - Sprites are drawn - Text is drawn - Primitives are drawn (rectangles, circles, etc.)

    The renderer is passed as a parameter so entities can draw themselves.

    "},{"location":"getting_started/fundamental_concepts/#cleanup","title":"Cleanup","text":"

    When you change scenes or end the game: - Entities from the previous scene can be cleaned up - Resources are freed - The new scene is initialized

    "},{"location":"getting_started/fundamental_concepts/#conceptual-summary","title":"Conceptual Summary","text":"

    To summarize, PixelRoot32 works like this:

    1. Engine coordinates everything and runs the game loop
    2. Scene organizes your game into screens/levels
    3. Entity is any object in your game
    4. Actor is an entity that can collide
    5. PhysicsActor is an actor with automatic physics
    6. Renderer draws everything on screen using layers
    7. Each frame updates logic and then draws

    All of this works automatically once you configure the Engine and create your scenes and entities. You don't need to worry about game loop details; you just need to implement update() and draw() in your entities.

    "},{"location":"getting_started/fundamental_concepts/#next-step","title":"Next Step","text":"

    Now that you understand the fundamental concepts, you're ready to create your first project and see these concepts in action with real code.

    See also: - What is PixelRoot32? - Why PixelRoot32? - Your First Project - Manual - Scenes and Entities

    "},{"location":"getting_started/installation/","title":"Installation","text":"

    This guide covers installing the PixelRoot32 documentation environment and preparing your development setup for ESP32 and Native (PC) targets.

    "},{"location":"getting_started/installation/#requirements","title":"Requirements","text":"
    • Python 3.11 or newer
    • Git (recommended for source management)
    • VS Code (or your preferred IDE)
    • For ESP32 targets: PlatformIO (VS Code extension) with ESP32 toolchain
    • For Native targets: a C++ build toolchain (CMake or your OS-native toolchain)
    "},{"location":"getting_started/installation/#install-documentation-tooling","title":"Install Documentation Tooling","text":"

    To build and preview this documentation locally:

    pip install mkdocs mkdocs-material mkdocs-minify-plugin mkdocs-git-revision-date-localized-plugin mike\nmkdocs serve\n

    Open http://127.0.0.1:8000 in your browser to preview.

    "},{"location":"getting_started/installation/#esp32-setup-recommended","title":"ESP32 Setup (Recommended)","text":"
    1. Install VS Code
    2. Install PlatformIO IDE extension
    3. Install ESP32 platform/toolchain via PlatformIO
    4. Clone the engine repository:
    5. https://github.com/Gperez88/PixelRoot32-Game-Engine
    6. Open the engine or example project in VS Code (PlatformIO)
    7. Build and upload to your ESP32 board

    Tip: Use boards based on ESP32-WROOM/WROVER for best compatibility. Ensure a reliable USB cable and correct serial port selection.

    "},{"location":"getting_started/installation/#native-pc-setup","title":"Native (PC) Setup","text":"
    1. Install a C++ toolchain (e.g., MSVC or MinGW on Windows)
    2. Install CMake (if the engine provides CMake build files)
    3. Clone the engine repository:
    4. https://github.com/Gperez88/PixelRoot32-Game-Engine
    5. Configure and build the native runtime:
    6. Follow the engine\u2019s native build instructions (Development \u2192 Compiling)
    "},{"location":"getting_started/installation/#verify-your-environment","title":"Verify Your Environment","text":"
    • ESP32: Build and flash a minimal sample; confirm serial output and display if applicable
    • Native: Run the executable; confirm window output and input handling
    "},{"location":"getting_started/installation/#troubleshooting","title":"Troubleshooting","text":"
    • If PlatformIO cannot find the ESP32 platform, update PlatformIO and retry
    • If native builds fail, verify compiler versions and CMake generator settings
    • Use Community \u2192 Troubleshooting for common issues and fixes
    "},{"location":"getting_started/installation/#next-steps","title":"Next Steps","text":"
    • First Project
    • Concepts
    "},{"location":"getting_started/what_is_pixelroot32/","title":"What is PixelRoot32?","text":"

    PixelRoot32 is a lightweight, modular 2D game engine written in C++ designed specifically for ESP32 microcontrollers, with a native simulation layer for PC (SDL2) that allows you to develop and debug quickly on your desktop before deploying to hardware.

    "},{"location":"getting_started/what_is_pixelroot32/#simple-definition","title":"Simple Definition","text":"

    PixelRoot32 is a game engine that lets you create retro-style 8-bit/16-bit video games directly on an ESP32 board, with the ability to develop and test on your PC before transferring code to hardware.

    "},{"location":"getting_started/what_is_pixelroot32/#key-features","title":"Key Features","text":""},{"location":"getting_started/what_is_pixelroot32/#scene-based-architecture","title":"\ud83c\udfae Scene-Based Architecture","text":"
    • Scene system inspired by Godot Engine
    • Intuitive management of levels, menus, and screens
    • Simple transitions between scenes
    "},{"location":"getting_started/what_is_pixelroot32/#optimized-rendering","title":"\ud83c\udfa8 Optimized Rendering","text":"
    • 1bpp (monochrome) sprites as the standard format
    • Support for multi-layer sprites (MultiSprite)
    • Experimental 2bpp and 4bpp formats for higher fidelity
    • Retro color palette system (NES, GameBoy, PICO-8, etc.)
    • Compact tilemaps for backgrounds and levels
    • 2D camera with dead-zone for smooth scrolling
    • Render layer system (background, gameplay, UI)
    "},{"location":"getting_started/what_is_pixelroot32/#nes-like-audio","title":"\ud83d\udd0a NES-like Audio","text":"
    • 4 audio channels (2 Pulse, 1 Triangle, 1 Noise)
    • Integrated sound effects system
    • Music player for background melodies
    • Backends for ESP32 (internal DAC or external I2S) and SDL2
    "},{"location":"getting_started/what_is_pixelroot32/#physics-and-collisions","title":"\ud83c\udfaf Physics and Collisions","text":"
    • AABB (Axis-Aligned Bounding Box) collision system
    • PhysicsActor with gravity, friction, and restitution
    • Collision layers and masks for fine control
    • World boundary collision detection
    "},{"location":"getting_started/what_is_pixelroot32/#user-interface","title":"\ud83d\udda5\ufe0f User Interface","text":"
    • Basic elements: Labels, Buttons, Panels
    • Automatic layouts: Vertical, Horizontal, Grid, Anchor
    • Integrated D-pad navigation
    • Scroll and viewport culling for long lists
    "},{"location":"getting_started/what_is_pixelroot32/#optimized-for-esp32","title":"\u26a1 Optimized for ESP32","text":"
    • Efficient memory management
    • Integrated object pooling
    • No dynamic allocations in the game loop
    • Performance optimized for limited hardware
    "},{"location":"getting_started/what_is_pixelroot32/#typical-use-cases","title":"Typical Use Cases","text":"

    PixelRoot32 is ideal for creating:

    • Arcade Games: Space Invaders, Pong, Breakout
    • Platformers: Horizontal scrolling games with simple physics
    • Puzzles: Tetris, Snake, logic games
    • Simple RPGs: Basic role-playing games with tilemaps
    • Shooters: Vertical or horizontal shooting games
    • Rapid Prototypes: Quick development of game ideas
    "},{"location":"getting_started/what_is_pixelroot32/#supported-platforms","title":"Supported Platforms","text":""},{"location":"getting_started/what_is_pixelroot32/#esp32","title":"ESP32","text":"
    • Display: TFT_eSPI (ST7735, ILI9341, ST7789, etc.)
    • Audio: Internal DAC (GPIO 25/26) or external I2S (MAX98357A, PCM5102)
    • Input: Digital buttons, D-pad
    • Hardware: Any ESP32 board (ESP32-WROOM, ESP32-WROVER, etc.)
    "},{"location":"getting_started/what_is_pixelroot32/#desktopnative-pc","title":"Desktop/Native (PC)","text":"
    • Display: SDL2 (Windows, Linux, macOS)
    • Audio: SDL2 Audio
    • Input: Keyboard, mouse
    • Usage: Development, debugging, testing

    Note: Support for u8g2 (OLEDs) is planned for the future.

    "},{"location":"getting_started/what_is_pixelroot32/#project-status","title":"Project Status","text":"

    Current Version: v0.2.0-dev

    PixelRoot32 is under active development. APIs may change and some subsystems are still experimental. Occasional changes or breaking changes are expected, especially on less-tested configurations.

    "},{"location":"getting_started/what_is_pixelroot32/#stable-features","title":"Stable Features","text":"
    • Scene and entity system
    • Basic rendering (1bpp sprites)
    • NES-like audio system
    • Basic physics and collisions
    • Basic UI system
    • ESP32 and Native support
    "},{"location":"getting_started/what_is_pixelroot32/#experimental-features","title":"Experimental Features","text":"
    • 2bpp and 4bpp sprites (require compilation flags)
    • Scene Arena (advanced memory management)
    "},{"location":"getting_started/what_is_pixelroot32/#planned-features","title":"Planned Features","text":"
    • Support for u8g2 (OLEDs)
    • Music compiler
    • Tilemap compiler
    • Save/load system
    • Spatial partitioning for collisions
    "},{"location":"getting_started/what_is_pixelroot32/#quick-comparison","title":"Quick Comparison","text":""},{"location":"getting_started/what_is_pixelroot32/#when-to-use-pixelroot32","title":"When to use PixelRoot32?","text":"

    \u2705 Use PixelRoot32 if: - You want to create retro games on ESP32 - You need a lightweight and efficient engine - You prefer a simple and clear architecture - You want to develop on PC and deploy to ESP32 - You like 8-bit/16-bit style games

    \u274c Don't use PixelRoot32 if: - You need 3D graphics - You require advanced shaders - You need complex physics (advanced physics engines) - You want to create modern AAA games - You need support for multiple mobile platforms

    "},{"location":"getting_started/what_is_pixelroot32/#next-step","title":"Next Step","text":"

    Now that you understand what PixelRoot32 is, discover why you should use it or go directly to your first project.

    See also: - Fundamental Concepts - Installation - API Reference

    "},{"location":"getting_started/why_pixelroot32/","title":"Why PixelRoot32?","text":"

    PixelRoot32 is specifically designed to solve the unique challenges of creating video games on embedded hardware like the ESP32, while maintaining the simplicity and productivity of modern development.

    "},{"location":"getting_started/why_pixelroot32/#main-advantages","title":"Main Advantages","text":""},{"location":"getting_started/why_pixelroot32/#optimized-for-esp32","title":"\ud83c\udfaf Optimized for ESP32","text":"

    Memory Efficient - 1bpp sprite system that minimizes RAM and Flash usage - Integrated object pooling to avoid memory fragmentation - Compact tilemaps that reuse sprites - No dynamic allocations in the game loop

    Performance Optimized - Rendering optimized for ESP32 limitations - Efficient render layer system - Viewport culling to reduce draw calls - Rendering pipeline designed for limited hardware

    Real Hardware - Direct support for common TFT displays (ST7735, ILI9341, ST7789) - Integrated audio (internal DAC or external I2S) - Simple pin and hardware configuration

    "},{"location":"getting_started/why_pixelroot32/#cross-platform-development","title":"\ud83d\udda5\ufe0f Cross-Platform Development","text":"

    Develop on PC, Deploy to ESP32 - Same code works on PC (SDL2) and ESP32 - Fast debugging on desktop - Testing without hardware needed - Rapid development iteration

    Visual Consistency - Native bitmap font system (pixel-perfect) - Same rendering on PC and ESP32 - Consistent color palettes - No surprises when transferring to hardware

    "},{"location":"getting_started/why_pixelroot32/#retro-palette-system","title":"\ud83c\udfa8 Retro Palette System","text":"

    Authentic Style - Predefined palettes: NES, GameBoy, GameBoy Color, PICO-8 - Dual palette mode for visual contrasts - Custom palettes for unique styles - Automatic color resolution (RGB565)

    Easy to Use - Change palette with one line of code - Consistent visualization across all sprites - No need to manually convert assets

    "},{"location":"getting_started/why_pixelroot32/#integrated-audio","title":"\ud83d\udd0a Integrated Audio","text":"

    Complete NES-like System - 4 audio channels (2 Pulse, 1 Triangle, 1 Noise) - Simple sound effects to create - Integrated music system - Backends for different hardware configurations

    No External Dependencies - Software-generated audio - No heavy audio libraries required - Full control over sound - Deterministic and predictable

    "},{"location":"getting_started/why_pixelroot32/#simple-and-clear-architecture","title":"\ud83c\udfd7\ufe0f Simple and Clear Architecture","text":"

    Easy to Understand - Intuitive scene system (inspired by Godot) - Clear hierarchy: Entity \u2192 Actor \u2192 PhysicsActor - Consistent and predictable APIs - Clean and well-organized code

    Quick to Learn - Familiar concepts for game developers - Clear documentation and complete examples - Smooth learning curve - Active community and support

    "},{"location":"getting_started/why_pixelroot32/#complete-features","title":"\ud83c\udfae Complete Features","text":"

    Everything Needed for Games - Rendering (sprites, tilemaps, primitives) - Audio (effects and music) - Physics (gravity, collisions, basic physics) - UI (layouts, buttons, navigation) - Input (buttons, keyboard) - Camera (scroll, parallax)

    No Bloat - Only the essentials, nothing more - No heavy dependencies - Small and maintainable codebase - Easy to understand and modify

    "},{"location":"getting_started/why_pixelroot32/#tools-and-ecosystem","title":"\ud83d\udee0\ufe0f Tools and Ecosystem","text":"

    Available Tools - Sprite Compiler to convert PNG to sprites - Complete game examples - Templates and starter code - Extensive documentation

    Community and Support - Active and developing project - Open source (MIT License) - Feedback and contributions welcome - Examples available

    "},{"location":"getting_started/why_pixelroot32/#comparison-with-alternatives","title":"Comparison with Alternatives","text":""},{"location":"getting_started/why_pixelroot32/#vs-full-engines-unity-godot-etc","title":"vs. Full Engines (Unity, Godot, etc.)","text":"

    PixelRoot32 Advantages: - \u2705 Much lighter (fits in ESP32) - \u2705 No unnecessary overhead - \u2705 Full control over code - \u2705 Specifically optimized for limited hardware

    Disadvantages: - \u274c Fewer advanced features - \u274c No visual editor - \u274c Fewer resources and community

    "},{"location":"getting_started/why_pixelroot32/#vs-writing-everything-from-scratch","title":"vs. Writing Everything from Scratch","text":"

    PixelRoot32 Advantages: - \u2705 Rendering system already implemented - \u2705 Integrated and working audio - \u2705 Physics and collisions ready - \u2705 Complete UI system - \u2705 Saves months of development

    Disadvantages: - \u274c Less control over internal implementation - \u274c You must learn the engine API

    "},{"location":"getting_started/why_pixelroot32/#vs-other-esp32-engines","title":"vs. Other ESP32 Engines","text":"

    PixelRoot32 Advantages: - \u2705 More modern and clear architecture - \u2705 Better documentation - \u2705 Unique palette system - \u2705 Integrated NES-like audio - \u2705 Real cross-platform development

    "},{"location":"getting_started/why_pixelroot32/#ideal-use-cases","title":"Ideal Use Cases","text":"

    PixelRoot32 is perfect for:

    1. Educational Projects
    2. Learn game development
    3. Understand engine architecture
    4. Student projects

    5. Rapid Prototypes

    6. Quickly validate game ideas
    7. Create demos and proof-of-concepts
    8. Test mechanics

    9. Retro Games

    10. 8-bit/16-bit style games
    11. Arcade games
    12. Games with retro aesthetics

    13. Hardware Projects

    14. Games on small displays
    15. DIY portable consoles
    16. Maker/retro projects

    17. C++ Learning

    18. Clean and well-structured code
    19. Good programming practices
    20. Real and functional examples
    "},{"location":"getting_started/why_pixelroot32/#limitations-to-consider","title":"Limitations to Consider","text":"

    To be honest, PixelRoot32 has limitations:

    • Limited Hardware: Designed for ESP32, not powerful PCs
    • Simple Graphics: No 3D, no advanced shaders
    • Basic Physics: Not a complete physics engine
    • Restricted Memory: MAX_ENTITIES = 32 per scene
    • In Development: Some features are experimental

    If you need advanced features or powerful hardware, consider other engines. But for retro games on ESP32, PixelRoot32 is an excellent choice.

    "},{"location":"getting_started/why_pixelroot32/#conclusion","title":"Conclusion","text":"

    PixelRoot32 combines:

    • \u2705 Simplicity of use
    • \u2705 Efficiency for limited hardware
    • \u2705 Completeness of essential features
    • \u2705 Clarity of architecture
    • \u2705 Productivity in development

    If you want to create retro games on ESP32 without the complexity of large engines, PixelRoot32 is the right choice.

    "},{"location":"getting_started/why_pixelroot32/#next-step","title":"Next Step","text":"

    Now that you understand why PixelRoot32 is a good option, learn the fundamental concepts or start directly with your first project.

    See also: - What is PixelRoot32? - Fundamental Concepts - Your First Project

    "},{"location":"getting_started/your_first_project/","title":"Your First Project","text":"

    This guide will walk you through creating and running your first PixelRoot32 project step by step. By the end, you'll have a working project that displays a simple scene on both ESP32 and PC.

    "},{"location":"getting_started/your_first_project/#prerequisites","title":"Prerequisites","text":""},{"location":"getting_started/your_first_project/#required-software","title":"Required Software","text":"
    • PlatformIO: Install the PlatformIO IDE extension in VS Code
    • Open VS Code
    • Go to Extensions (Ctrl+Shift+X)
    • Search for \"PlatformIO IDE\"
    • Install and restart VS Code

    • Python 3.8+: Required for PlatformIO (usually installed automatically)

    "},{"location":"getting_started/your_first_project/#for-esp32-development","title":"For ESP32 Development","text":"
    • ESP32 Board: Any ESP32 development board (ESP32-WROOM, ESP32-WROVER, etc.)
    • USB Cable: To connect and program your ESP32
    • TFT Display: Compatible display (ST7735, ST7789, ILI9341, etc.)
    • Buttons: 5-6 digital buttons for input (optional for first project)
    • Audio Hardware (optional): Speaker + amplifier (PAM8302A) or I2S DAC (MAX98357A)
    "},{"location":"getting_started/your_first_project/#for-native-pc-development","title":"For Native (PC) Development","text":"
    • SDL2: Development libraries
    • Windows (MSYS2): pacman -S mingw-w64-x86_64-SDL2
    • Linux: sudo apt-get install libsdl2-dev
    • macOS: brew install sdl2
    "},{"location":"getting_started/your_first_project/#step-1-create-a-new-platformio-project","title":"Step 1: Create a New PlatformIO Project","text":"
    1. Open VS Code with PlatformIO installed

    2. Create New Project:

    3. Click on the PlatformIO icon in the sidebar
    4. Click \"New Project\"
    5. Name: my-first-pixelroot32-game
    6. Board: Select \"ESP32 Dev Module\" (or your specific board)
    7. Framework: Arduino
    8. Location: Choose your workspace folder
    9. Click \"Finish\"

    10. Project Structure: Your project should now have this structure:

      my-first-pixelroot32-game/\n\u251c\u2500\u2500 .pio/\n\u251c\u2500\u2500 include/\n\u251c\u2500\u2500 lib/\n\u251c\u2500\u2500 src/\n\u2502   \u2514\u2500\u2500 main.cpp\n\u251c\u2500\u2500 test/\n\u2514\u2500\u2500 platformio.ini\n

    "},{"location":"getting_started/your_first_project/#step-2-install-pixelroot32-engine","title":"Step 2: Install PixelRoot32 Engine","text":""},{"location":"getting_started/your_first_project/#option-a-via-platformio-library-manager-recommended","title":"Option A: Via PlatformIO Library Manager (Recommended)","text":"
    1. Open platformio.ini

    2. Add the library dependency:

    [env:esp32dev]\nplatform = espressif32\nboard = esp32dev\nframework = arduino\nlib_deps = \n    gperez88/PixelRoot32-Game-Engine@0.2.0-dev\n

    \u26a0\ufe0f IMPORTANT: Use the exact version 0.2.0-dev. Do NOT use ^ or fuzzy versioning.

    1. Save the file. PlatformIO will automatically download the library.
    "},{"location":"getting_started/your_first_project/#option-b-git-submodule","title":"Option B: Git Submodule","text":"
    1. Open terminal in your project root

    2. Add as submodule:

      git submodule add https://github.com/Gperez88/PixelRoot32-Game-Engine.git lib/PixelRoot32-Game-Engine\n

    3. Update platformio.ini:

      lib_extra_dirs = lib\n

    "},{"location":"getting_started/your_first_project/#step-3-configure-hardware-esp32","title":"Step 3: Configure Hardware (ESP32)","text":""},{"location":"getting_started/your_first_project/#configure-tft_espi-display","title":"Configure TFT_eSPI Display","text":"

    Edit platformio.ini and add build flags for your display. Here are two common configurations:

    For ST7789 (240x240):

    [env:esp32dev]\nplatform = espressif32\nboard = esp32dev\nframework = arduino\nlib_deps = \n    gperez88/PixelRoot32-Game-Engine@0.2.0-dev\n    bodmer/TFT_eSPI@^2.5.43\n\nbuild_flags = \n    -D ST7789_DRIVER\n    -D TFT_WIDTH=240\n    -D TFT_HEIGHT=240\n    -D TFT_MOSI=23\n    -D TFT_SCLK=18\n    -D TFT_DC=2\n    -D TFT_RST=4\n    -D TFT_CS=-1\n    -D LOAD_GLCD\n    -D LOAD_FONT2\n    -D LOAD_FONT4\n    -D LOAD_FONT6\n    -D LOAD_FONT7\n    -D LOAD_FONT8\n    -D LOAD_GFXFF\n    -D SMOOTH_FONT\n    -D SPI_FREQUENCY=40000000\n    -D SPI_READ_FREQUENCY=20000000\n

    For ST7735 (128x128):

    build_flags = \n    -D ST7735_DRIVER\n    -D ST7735_GREENTAB3\n    -D TFT_WIDTH=128\n    -D TFT_HEIGHT=128\n    -D TFT_MOSI=23\n    -D TFT_SCLK=18\n    -D TFT_DC=2\n    -D TFT_RST=4\n    -D TFT_CS=-1\n    -D LOAD_GLCD\n    -D LOAD_FONT2\n    -D LOAD_FONT4\n    -D LOAD_FONT6\n    -D LOAD_FONT7\n    -D LOAD_FONT8\n    -D LOAD_GFXFF\n    -D SMOOTH_FONT\n    -D SPI_FREQUENCY=27000000\n    -D SPI_READ_FREQUENCY=20000000\n

    Note: Adjust the pin numbers (TFT_MOSI, TFT_SCLK, TFT_DC, TFT_RST) to match your hardware wiring.

    "},{"location":"getting_started/your_first_project/#configure-input-optional-for-first-project","title":"Configure Input (Optional for First Project)","text":"

    If you have buttons connected, note the GPIO pins. For now, we'll create a project that works without input.

    "},{"location":"getting_started/your_first_project/#configure-audio-optional-for-first-project","title":"Configure Audio (Optional for First Project)","text":"

    Audio is optional for the first project. We'll add it later.

    "},{"location":"getting_started/your_first_project/#step-4-create-your-first-scene","title":"Step 4: Create Your First Scene","text":"

    Create a new file src/MyFirstScene.h:

    #pragma once\n#include <core/Scene.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\nclass MyFirstScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Called when the scene is initialized\n        // Set up your scene here\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Called every frame\n        // Update game logic here\n        Scene::update(deltaTime); // Don't forget to call parent update!\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Called every frame to draw\n        // Draw your scene here\n\n        // Example: Draw a simple rectangle\n        renderer.drawFilledRectangle(50, 50, 100, 100, pixelroot32::graphics::Color::Blue);\n\n        // Example: Draw text\n        renderer.drawText(\"Hello PixelRoot32!\", 20, 20, pixelroot32::graphics::Color::White, 2);\n\n        // Don't forget to call parent draw to draw all entities!\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"getting_started/your_first_project/#step-5-create-main-file-esp32","title":"Step 5: Create Main File (ESP32)","text":"

    Replace the contents of src/main.cpp with:

    #include <Arduino.h>\n#include <core/Engine.h>\n#include <drivers/esp32/TFT_eSPI_Drawer.h>\n#include <drivers/esp32/ESP32_DAC_AudioBackend.h>\n#include \"MyFirstScene.h\"\n\nnamespace pr32 = pixelroot32;\n\n// Audio configuration (optional - can be omitted for first project)\nconst int DAC_PIN = 25; // GPIO 25 or 26\npr32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(DAC_PIN, 11025);\n\n// Display configuration\n// ST7789, rotation 0, 240x240 resolution\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::ST7789, \n    0,      // rotation\n    240,    // width\n    240     // height\n);\n\n// Input configuration (6 buttons: UP, DOWN, LEFT, RIGHT, A, B)\n// For now, we'll use dummy pins - you can change these later\npr32::input::InputConfig inputConfig(\n    6,      // button count\n    32,     // UP pin\n    27,     // DOWN pin\n    33,     // LEFT pin\n    14,     // RIGHT pin\n    13,     // A button pin\n    12      // B button pin\n);\n\n// Audio configuration\npr32::audio::AudioConfig audioConfig(&audioBackend, audioBackend.getSampleRate());\n\n// Create the engine\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\n// Create your scene\nMyFirstScene myScene;\n\nvoid setup() {\n    Serial.begin(115200);\n\n    // Initialize the engine\n    engine.init();\n\n    // Initialize and set the scene\n    myScene.init();\n    engine.setScene(&myScene);\n\n    Serial.println(\"PixelRoot32 initialized!\");\n}\n\nvoid loop() {\n    // Run the game loop\n    engine.run();\n}\n
    "},{"location":"getting_started/your_first_project/#step-6-create-native-version-optional","title":"Step 6: Create Native Version (Optional)","text":"

    If you want to test on PC first, create src/main_native.cpp:

    #define SDL_MAIN_HANDLED\n#include <SDL2/SDL.h>\n#include <core/Engine.h>\n#include <drivers/native/SDL2_Drawer.h>\n#include <drivers/native/SDL2_AudioBackend.h>\n#include \"MyFirstScene.h\"\n\nnamespace pr32 = pixelroot32;\n\n// Audio configuration\npr32::drivers::native::SDL2_AudioBackend audioBackend(22050, 1024);\n\n// Display configuration (NONE defaults to SDL2 on Native)\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::NONE,\n    0,      // rotation\n    240,    // width\n    240     // height\n);\n\n// Input configuration (SDL scancodes)\npr32::input::InputConfig inputConfig(\n    6,                      // button count\n    SDL_SCANCODE_UP,        // UP\n    SDL_SCANCODE_DOWN,      // DOWN\n    SDL_SCANCODE_LEFT,      // LEFT\n    SDL_SCANCODE_RIGHT,     // RIGHT\n    SDL_SCANCODE_SPACE,     // A button\n    SDL_SCANCODE_RETURN     // B button\n);\n\n// Audio configuration\npr32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n\n// Create the engine\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\n// Create your scene\nMyFirstScene myScene;\n\nint main(int argc, char* argv[]) {\n    (void)argc;\n    (void)argv;\n\n    // Initialize the engine\n    engine.init();\n\n    // Initialize and set the scene\n    myScene.init();\n    engine.setScene(&myScene);\n\n    // Run the game loop\n    engine.run();\n\n    return 0;\n}\n
    "},{"location":"getting_started/your_first_project/#configure-native-build","title":"Configure Native Build","text":"

    Add to platformio.ini:

    [env:native]\nplatform = native\nbuild_src_filter = \n    +<*>\n    -<main.cpp>\nlib_extra_dirs = lib\nbuild_flags = \n    -D PLATFORM_NATIVE\n    -Isrc\n    -Ilib/PixelRoot32-Game-Engine/include\n    -IC:/msys64/mingw64/include/SDL2    # Windows MSYS2 path - adjust for your system\n    -LC:/msys64/mingw64/lib             # Windows MSYS2 path - adjust for your system\n    -O2\n    -Wall\n    -Wextra\n    -std=c++17\n    -lSDL2\n    -mconsole\n

    Note: Adjust the SDL2 include and library paths for your system.

    "},{"location":"getting_started/your_first_project/#step-7-build-and-run","title":"Step 7: Build and Run","text":""},{"location":"getting_started/your_first_project/#for-esp32","title":"For ESP32","text":"
    1. Connect your ESP32 via USB
    2. Select the environment: Click on the PlatformIO icon \u2192 Select env:esp32dev
    3. Build: Click the checkmark icon (\u2713) or press Ctrl+Alt+B
    4. Upload: Click the arrow icon (\u2192) or press Ctrl+Alt+U
    5. Monitor: Click the plug icon to open serial monitor

    You should see \"PixelRoot32 initialized!\" in the serial monitor and your display should show a blue rectangle and text.

    "},{"location":"getting_started/your_first_project/#for-native-pc","title":"For Native (PC)","text":"
    1. Select the environment: Click on the PlatformIO icon \u2192 Select env:native
    2. Build and Run: Click the play icon (\u25b6) or press Ctrl+Alt+R

    A window should open showing your scene with a blue rectangle and \"Hello PixelRoot32!\" text.

    "},{"location":"getting_started/your_first_project/#step-8-verify-it-works","title":"Step 8: Verify It Works","text":"

    If everything is set up correctly, you should see:

    • ESP32: Display shows a blue rectangle at (50, 50) and white text \"Hello PixelRoot32!\" at (20, 20)
    • Native: Window shows the same content

    If you see this, congratulations! Your first PixelRoot32 project is working.

    "},{"location":"getting_started/your_first_project/#troubleshooting","title":"Troubleshooting","text":""},{"location":"getting_started/your_first_project/#esp32-issues","title":"ESP32 Issues","text":"

    Display is blank: - Check wiring connections - Verify pin numbers in platformio.ini match your hardware - Check SPI frequency (try lowering it) - Verify display type (ST7789 vs ST7735)

    Compilation errors: - Ensure library version is exactly 0.2.0-dev - Check that TFT_eSPI is installed - Verify all include paths are correct

    Upload fails: - Check USB cable connection - Try different USB port - Press BOOT button on ESP32 during upload - Check COM port in PlatformIO

    "},{"location":"getting_started/your_first_project/#native-issues","title":"Native Issues","text":"

    SDL2 not found: - Verify SDL2 is installed - Check include/library paths in platformio.ini - On Windows, ensure MSYS2 paths are correct

    Window doesn't open: - Check console for error messages - Verify SDL2 is properly linked - Try running from terminal to see errors

    "},{"location":"getting_started/your_first_project/#next-steps","title":"Next Steps","text":"

    Now that you have a working project, you can:

    1. Learn about Scenes and Entities: See how to create game objects
    2. Add Input: Make your scene respond to buttons
    3. Add Sprites: Draw custom graphics
    4. Add Audio: Play sounds and music

    Continue with the Development Guide to learn more.

    See also: - Fundamental Concepts - Installation - Manual - Scenes and Entities - API Reference

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/","title":"Cameras and Scrolling","text":"

    Camera2D allows you to create worlds larger than the screen by scrolling the view. This guide covers camera setup, following targets, boundaries, and parallax effects.

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#camera2d-basics","title":"Camera2D Basics","text":"

    A Camera2D defines what portion of your game world is visible on screen.

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#creating-a-camera","title":"Creating a Camera","text":"
    #include <graphics/Camera2D.h>\n\n// Create camera with viewport size\npixelroot32::graphics::Camera2D camera(240, 240); // Screen width, height\n\n// Set camera position\ncamera.setPosition(0, 0);\n\n// Apply camera to renderer (in draw method)\ncamera.apply(renderer);\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#how-it-works","title":"How It Works","text":"

    The camera translates world coordinates to screen coordinates: - Objects at world position (100, 50) with camera at (0, 0) appear at screen (100, 50) - Objects at world position (100, 50) with camera at (50, 0) appear at screen (50, 50) - The camera effectively \"moves\" the world relative to the screen

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#following-a-target","title":"Following a Target","text":"

    The most common use is following a player or other target.

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#basic-follow","title":"Basic Follow","text":"
    class GameScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    PlayerActor* player;\n\npublic:\n    void init() override {\n        int screenWidth = engine.getRenderer().getWidth();\n        int screenHeight = engine.getRenderer().getHeight();\n\n        // Create camera\n        camera = pixelroot32::graphics::Camera2D(screenWidth, screenHeight);\n\n        // Create player\n        player = new PlayerActor(500, 300); // World position\n        addEntity(player);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Make camera follow player\n        camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Apply camera before drawing\n        camera.apply(renderer);\n\n        // Now all drawing uses camera coordinates\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#dead-zone-smooth-following","title":"Dead Zone (Smooth Following)","text":"

    For smoother following, you can implement a dead zone where the camera doesn't move until the target leaves the zone:

    void update(unsigned long deltaTime) override {\n    Scene::update(deltaTime);\n\n    // Get screen center\n    int screenCenterX = engine.getRenderer().getWidth() / 2;\n    int screenCenterY = engine.getRenderer().getHeight() / 2;\n\n    // Calculate player position relative to screen center\n    float playerScreenX = player->x - camera.getX();\n    float playerScreenY = player->y - camera.getY();\n\n    // Dead zone size\n    const int DEAD_ZONE_X = 40;\n    const int DEAD_ZONE_Y = 40;\n\n    // Move camera if player leaves dead zone\n    if (playerScreenX < screenCenterX - DEAD_ZONE_X) {\n        camera.setPosition(player->x - (screenCenterX - DEAD_ZONE_X), camera.getY());\n    } else if (playerScreenX > screenCenterX + DEAD_ZONE_X) {\n        camera.setPosition(player->x - (screenCenterX + DEAD_ZONE_X), camera.getY());\n    }\n\n    if (playerScreenY < screenCenterY - DEAD_ZONE_Y) {\n        camera.setPosition(camera.getX(), player->y - (screenCenterY - DEAD_ZONE_Y));\n    } else if (playerScreenY > screenCenterY + DEAD_ZONE_Y) {\n        camera.setPosition(camera.getX(), player->y - (screenCenterY + DEAD_ZONE_Y));\n    }\n}\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#camera-boundaries","title":"Camera Boundaries","text":"

    Limit camera movement to keep it within your level bounds.

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#setting-boundaries","title":"Setting Boundaries","text":"
    void init() override {\n    // Create camera\n    camera = pixelroot32::graphics::Camera2D(240, 240);\n\n    // Set horizontal boundaries (level is 2000 pixels wide)\n    camera.setBounds(0, 2000 - 240); // minX, maxX\n\n    // Set vertical boundaries (level is 1000 pixels tall)\n    camera.setVerticalBounds(0, 1000 - 240); // minY, maxY\n}\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#example-side-scroller-with-boundaries","title":"Example: Side-Scroller with Boundaries","text":"
    class SideScrollerScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    PlayerActor* player;\n    static const int LEVEL_WIDTH = 2000;\n    static const int LEVEL_HEIGHT = 240;\n\npublic:\n    void init() override {\n        int screenWidth = engine.getRenderer().getWidth();\n        int screenHeight = engine.getRenderer().getHeight();\n\n        camera = pixelroot32::graphics::Camera2D(screenWidth, screenHeight);\n\n        // Set boundaries (camera can't go outside level)\n        camera.setBounds(0, LEVEL_WIDTH - screenWidth);\n        camera.setVerticalBounds(0, LEVEL_HEIGHT - screenHeight);\n\n        // Create player at start\n        player = new PlayerActor(100, 100);\n        addEntity(player);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Follow player horizontally\n        camera.followTarget(player->x, camera.getY());\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        camera.apply(renderer);\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#parallax-scrolling","title":"Parallax Scrolling","text":"

    Parallax creates depth by moving background layers at different speeds.

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#basic-parallax","title":"Basic Parallax","text":"
    class ParallaxBackground : public pixelroot32::core::Entity {\nprivate:\n    float parallaxSpeed; // 0.0 to 1.0 (1.0 = normal, 0.5 = half speed)\n    float baseX;\n\npublic:\n    ParallaxBackground(float speed)\n        : Entity(0, 0, 240, 240, pixelroot32::core::EntityType::GENERIC),\n          parallaxSpeed(speed), baseX(0) {\n        setRenderLayer(0);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Get camera position\n        auto& camera = getCamera(); // You'll need to pass camera reference\n\n        // Calculate parallax offset\n        baseX = camera.getX() * parallaxSpeed;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw background with parallax offset\n        renderer.drawTileMap(backgroundTileMap, \n            static_cast<int>(baseX), 0, \n            pixelroot32::graphics::Color::White);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#multiple-parallax-layers","title":"Multiple Parallax Layers","text":"
    class ParallaxScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n\n    // Parallax layers (farther = slower)\n    ParallaxLayer* farBackground;    // Speed: 0.2\n    ParallaxLayer* midBackground;      // Speed: 0.5\n    ParallaxLayer* nearBackground;     // Speed: 0.8\n    PlayerActor* player;               // Speed: 1.0 (normal)\n\npublic:\n    void init() override {\n        camera = pixelroot32::graphics::Camera2D(240, 240);\n\n        // Create parallax layers\n        farBackground = new ParallaxLayer(0.2f);  // Moves slowest\n        midBackground = new ParallaxLayer(0.5f);\n        nearBackground = new ParallaxLayer(0.8f);\n\n        addEntity(farBackground);\n        addEntity(midBackground);\n        addEntity(nearBackground);\n\n        player = new PlayerActor(100, 100);\n        addEntity(player);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Update parallax layers with camera position\n        farBackground->updateParallax(camera.getX());\n        midBackground->updateParallax(camera.getX());\n        nearBackground->updateParallax(camera.getX());\n\n        // Follow player\n        camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        camera.apply(renderer);\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#using-setdisplayoffset-for-parallax","title":"Using setDisplayOffset for Parallax","text":"

    For simpler parallax, you can use setDisplayOffset():

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Apply camera\n    camera.apply(renderer);\n\n    // Draw far background with offset (moves slower)\n    renderer.setDisplayOffset(\n        static_cast<int>(camera.getX() * 0.3f), \n        0\n    );\n    renderer.drawTileMap(farBackground, 0, 0, Color::White);\n\n    // Draw mid background\n    renderer.setDisplayOffset(\n        static_cast<int>(camera.getX() * 0.6f), \n        0\n    );\n    renderer.drawTileMap(midBackground, 0, 0, Color::White);\n\n    // Reset offset for normal drawing\n    renderer.setDisplayOffset(0, 0);\n\n    // Draw game objects (normal speed)\n    Scene::draw(renderer);\n}\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#complete-example-platformer-with-camera","title":"Complete Example: Platformer with Camera","text":"
    class PlatformerScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    PlayerActor* player;\n    static const int LEVEL_WIDTH = 3000;\n    static const int LEVEL_HEIGHT = 800;\n\npublic:\n    void init() override {\n        int screenWidth = engine.getRenderer().getWidth();\n        int screenHeight = engine.getRenderer().getHeight();\n\n        // Create camera\n        camera = pixelroot32::graphics::Camera2D(screenWidth, screenHeight);\n\n        // Set boundaries\n        camera.setBounds(0, LEVEL_WIDTH - screenWidth);\n        camera.setVerticalBounds(0, LEVEL_HEIGHT - screenHeight);\n\n        // Create player\n        player = new PlayerActor(100, 400);\n        addEntity(player);\n\n        // Create platforms, enemies, etc.\n        // ...\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Follow player with dead zone\n        int screenCenterX = engine.getRenderer().getWidth() / 2;\n        int screenCenterY = engine.getRenderer().getHeight() / 2;\n\n        float playerScreenX = player->x - camera.getX();\n        float playerScreenY = player->y - camera.getY();\n\n        const int DEAD_ZONE = 60;\n\n        // Horizontal follow\n        if (playerScreenX < screenCenterX - DEAD_ZONE) {\n            camera.setPosition(player->x - (screenCenterX - DEAD_ZONE), camera.getY());\n        } else if (playerScreenX > screenCenterX + DEAD_ZONE) {\n            camera.setPosition(player->x - (screenCenterX + DEAD_ZONE), camera.getY());\n        }\n\n        // Vertical follow (only when falling or jumping high)\n        if (playerScreenY < screenCenterY - DEAD_ZONE || \n            playerScreenY > screenCenterY + DEAD_ZONE) {\n            camera.setPosition(camera.getX(), player->y - screenCenterY);\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Apply camera\n        camera.apply(renderer);\n\n        // Draw background (parallax)\n        renderer.setDisplayOffset(\n            static_cast<int>(camera.getX() * 0.3f), \n            0\n        );\n        renderer.drawTileMap(backgroundTileMap, 0, 0, Color::DarkGray);\n        renderer.setDisplayOffset(0, 0);\n\n        // Draw game objects\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#best-practices","title":"Best Practices","text":""},{"location":"manual/advanced_graphics/cameras_and_scrolling/#camera-movement","title":"Camera Movement","text":"
    • Use dead zones: Prevents jittery camera movement
    • Smooth transitions: Consider lerping camera position for smoother movement
    • Set boundaries: Always limit camera to level bounds
    • Test on hardware: Camera performance may differ on ESP32
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#parallax","title":"Parallax","text":"
    • Layer speeds: Farther layers move slower (0.2-0.5), closer move faster (0.7-0.9)
    • Limit layers: Too many parallax layers can impact performance
    • Use tilemaps: Parallax works best with tilemaps
    • Test visually: Ensure parallax effect is noticeable but not distracting
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#performance","title":"Performance","text":"
    • Apply once: Call camera.apply() once per frame, at start of draw()
    • Cull off-screen: Don't draw entities outside camera view
    • Limit parallax layers: 2-3 layers is usually enough
    • Optimize tilemaps: Use efficient tilemap rendering
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/advanced_graphics/cameras_and_scrolling/#camera-helper-class","title":"Camera Helper Class","text":"
    class CameraController {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    float targetX, targetY;\n    float smoothSpeed = 0.1f;\n\npublic:\n    void followTarget(float x, float y) {\n        targetX = x;\n        targetY = y;\n    }\n\n    void update(unsigned long deltaTime) {\n        // Smooth camera movement\n        float currentX = camera.getX();\n        float currentY = camera.getY();\n\n        float newX = currentX + (targetX - currentX) * smoothSpeed;\n        float newY = currentY + (targetY - currentY) * smoothSpeed;\n\n        camera.setPosition(newX, newY);\n    }\n\n    void apply(pixelroot32::graphics::Renderer& renderer) {\n        camera.apply(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#viewport-culling","title":"Viewport Culling","text":"

    Only draw entities within camera view:

    bool isVisible(float x, float y, int width, int height) {\n    float cameraX = camera.getX();\n    float cameraY = camera.getY();\n    int screenWidth = engine.getRenderer().getWidth();\n    int screenHeight = engine.getRenderer().getHeight();\n\n    return !(x + width < cameraX || \n             x > cameraX + screenWidth ||\n             y + height < cameraY || \n             y > cameraY + screenHeight);\n}\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/advanced_graphics/cameras_and_scrolling/#camera-not-moving","title":"Camera Not Moving","text":"
    • Verify camera.apply() is called in draw()
    • Check followTarget() or setPosition() is called in update()
    • Ensure camera is created with correct viewport size
    • Check boundaries aren't preventing movement
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#objects-not-visible","title":"Objects Not Visible","text":"
    • Verify objects are within camera view
    • Check world coordinates vs screen coordinates
    • Ensure camera is applied before drawing
    • Verify render layers are correct
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#parallax-not-working","title":"Parallax Not Working","text":"
    • Check setDisplayOffset() is used correctly
    • Verify parallax speed values (0.0 to 1.0)
    • Ensure offset is reset after parallax layers
    • Test with different speed values
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#next-steps","title":"Next Steps","text":"

    Now that you understand cameras and scrolling, learn about: - Tilemaps - Build levels with tiles - Particles and Effects - Add visual effects - Performance Optimization - Optimize your game

    See also: - API Reference - Camera2D - Manual - Basic Rendering - Manual - Tilemaps

    "},{"location":"manual/advanced_graphics/color_palettes/","title":"Color Palettes","text":"

    PixelRoot32 uses a palette-based color system that allows you to easily change the visual style of your game. This guide covers built-in palettes, dual palette mode, and custom palettes.

    "},{"location":"manual/advanced_graphics/color_palettes/#built-in-palettes","title":"Built-in Palettes","text":"

    PixelRoot32 includes several predefined palettes inspired by classic gaming systems:

    "},{"location":"manual/advanced_graphics/color_palettes/#available-palettes","title":"Available Palettes","text":"
    #include <graphics/PaletteDefs.h>\n\nnamespace pixelroot32::graphics {\n\nenum class PaletteType {\n    PR32,    // PixelRoot32 default palette\n    NES,     // Nintendo Entertainment System\n    GB,      // GameBoy (4 shades of green)\n    GBC,     // GameBoy Color\n    PICO8    // PICO-8 palette\n};\n
    "},{"location":"manual/advanced_graphics/color_palettes/#using-built-in-palettes","title":"Using Built-in Palettes","text":"
    #include <graphics/PaletteDefs.h>\n\n// Set palette globally (legacy mode)\npixelroot32::graphics::setPalette(pixelroot32::graphics::PaletteType::NES);\n\n// All sprites will now use NES colors\nrenderer.drawSprite(MY_SPRITE, 100, 100, pixelroot32::graphics::Color::White);\n
    "},{"location":"manual/advanced_graphics/color_palettes/#palette-characteristics","title":"Palette Characteristics","text":"

    PR32 (Default) - Modern, balanced colors - Good contrast - Suitable for most games

    NES - Classic 8-bit console colors - Limited color range - Nostalgic feel

    GB (GameBoy) - 4 shades of green - Monochrome aesthetic - Classic handheld look

    GBC (GameBoy Color) - Expanded color range - More vibrant than GB - Classic portable console

    PICO8 - PICO-8 fantasy console palette - 16 carefully chosen colors - Popular for retro games

    "},{"location":"manual/advanced_graphics/color_palettes/#legacy-mode-single-global-palette","title":"Legacy Mode (Single Global Palette)","text":"

    In legacy mode, one palette is used for all sprites:

    void MyScene::init() override {\n    // Set global palette\n    pixelroot32::graphics::setPalette(pixelroot32::graphics::PaletteType::NES);\n\n    // All sprites use NES colors\n    // This is the simplest mode\n}\n

    When to use: - Simple games - Consistent visual style - Maximum compatibility

    "},{"location":"manual/advanced_graphics/color_palettes/#dual-palette-mode","title":"Dual Palette Mode","text":"

    Dual palette mode allows different palettes for background elements and sprites, creating visual contrast.

    "},{"location":"manual/advanced_graphics/color_palettes/#enabling-dual-palette-mode","title":"Enabling Dual Palette Mode","text":"
    #include <graphics/PaletteDefs.h>\n\nvoid MyScene::init() override {\n    // Enable dual palette mode\n    pixelroot32::graphics::enableDualPaletteMode();\n\n    // Set background palette\n    pixelroot32::graphics::setBackgroundPalette(\n        pixelroot32::graphics::PaletteType::GB\n    );\n\n    // Set sprite palette\n    pixelroot32::graphics::setSpritePalette(\n        pixelroot32::graphics::PaletteType::NES\n    );\n}\n
    "},{"location":"manual/advanced_graphics/color_palettes/#how-it-works","title":"How It Works","text":"
    • Background palette: Used for tilemaps, primitives, and background sprites
    • Sprite palette: Used for game objects, characters, and foreground sprites
    • Automatic context: The renderer automatically selects the correct palette based on what you're drawing
    "},{"location":"manual/advanced_graphics/color_palettes/#example-contrasting-styles","title":"Example: Contrasting Styles","text":"
    void MyScene::init() override {\n    pixelroot32::graphics::enableDualPaletteMode();\n\n    // Dark, muted background (GameBoy green)\n    pixelroot32::graphics::setBackgroundPalette(\n        pixelroot32::graphics::PaletteType::GB\n    );\n\n    // Bright, colorful sprites (NES)\n    pixelroot32::graphics::setSpritePalette(\n        pixelroot32::graphics::PaletteType::NES\n    );\n\n    // Background uses GB palette\n    renderer.drawTileMap(backgroundTileMap, 0, 0, \n        pixelroot32::graphics::Color::White);\n\n    // Sprites use NES palette\n    renderer.drawSprite(playerSprite, 100, 100, \n        pixelroot32::graphics::Color::White);\n}\n
    "},{"location":"manual/advanced_graphics/color_palettes/#when-to-use-dual-palette-mode","title":"When to Use Dual Palette Mode","text":"
    • Visual contrast: Make sprites stand out from background
    • Artistic style: Different palettes for different layers
    • Retro aesthetics: Classic console color separation
    • Performance: No performance impact, just visual variety
    "},{"location":"manual/advanced_graphics/color_palettes/#custom-palettes","title":"Custom Palettes","text":"

    Create your own color palettes for unique visual styles.

    "},{"location":"manual/advanced_graphics/color_palettes/#creating-a-custom-palette","title":"Creating a Custom Palette","text":"
    #include <graphics/PaletteDefs.h>\n#include <graphics/Color.h>\n\n// Define custom colors (RGB565 format)\nstatic const pixelroot32::graphics::Color CUSTOM_PALETTE[] = {\n    pixelroot32::graphics::Color::Black,      // 0: Transparent/background\n    pixelroot32::graphics::Color::DarkBlue,   // 1\n    pixelroot32::graphics::Color::Blue,       // 2\n    pixelroot32::graphics::Color::LightBlue, // 3\n    pixelroot32::graphics::Color::Cyan,      // 4\n    pixelroot32::graphics::Color::White,      // 5\n    // ... more colors\n};\n\n// Set custom palette\npixelroot32::graphics::setCustomPalette(\n    CUSTOM_PALETTE,\n    sizeof(CUSTOM_PALETTE) / sizeof(pixelroot32::graphics::Color)\n);\n
    "},{"location":"manual/advanced_graphics/color_palettes/#rgb565-color-format","title":"RGB565 Color Format","text":"

    Colors in PixelRoot32 use RGB565 format (16-bit):

    // RGB565: 5 bits red, 6 bits green, 5 bits blue\n// Format: RRRRR GGGGGG BBBBB\n\n// Create custom RGB565 color\nuint16_t myColor = (31 << 11) | (63 << 5) | 31; // White\nuint16_t myColor = (0 << 11) | (0 << 5) | 0;    // Black\nuint16_t myColor = (31 << 11) | (0 << 5) | 0;   // Red\n\n// Or use Color constants\npixelroot32::graphics::Color::Red\npixelroot32::graphics::Color::Green\npixelroot32::graphics::Color::Blue\n
    "},{"location":"manual/advanced_graphics/color_palettes/#helper-function-for-custom-colors","title":"Helper Function for Custom Colors","text":"
    // Create RGB565 color from RGB values (0-255)\nuint16_t rgb565(uint8_t r, uint8_t g, uint8_t b) {\n    return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);\n}\n\n// Usage\nstatic const pixelroot32::graphics::Color MY_PALETTE[] = {\n    rgb565(0, 0, 0),        // Black\n    rgb565(255, 0, 0),      // Red\n    rgb565(0, 255, 0),      // Green\n    rgb565(0, 0, 255),      // Blue\n    rgb565(255, 255, 255),  // White\n};\n
    "},{"location":"manual/advanced_graphics/color_palettes/#complete-custom-palette-example","title":"Complete Custom Palette Example","text":"
    class CustomPaletteScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Define custom palette (ocean theme)\n        static const pixelroot32::graphics::Color OCEAN_PALETTE[] = {\n            pixelroot32::graphics::Color::Black,      // 0: Deep ocean\n            pixelroot32::graphics::Color::Navy,        // 1: Dark blue\n            pixelroot32::graphics::Color::Blue,       // 2: Medium blue\n            pixelroot32::graphics::Color::Cyan,       // 3: Light blue\n            pixelroot32::graphics::Color::LightBlue, // 4: Surface\n            pixelroot32::graphics::Color::White,      // 5: Foam\n        };\n\n        // Set custom palette\n        pixelroot32::graphics::setCustomPalette(\n            OCEAN_PALETTE,\n            sizeof(OCEAN_PALETTE) / sizeof(pixelroot32::graphics::Color)\n        );\n\n        // Now all sprites use the ocean palette\n    }\n};\n
    "},{"location":"manual/advanced_graphics/color_palettes/#color-constants","title":"Color Constants","text":"

    PixelRoot32 provides predefined color constants:

    namespace pixelroot32::graphics {\n    Color::Black\n    Color::White\n    Color::Red\n    Color::Green\n    Color::Blue\n    Color::Yellow\n    Color::Cyan\n    Color::Magenta\n    Color::DarkGray\n    Color::LightGray\n    Color::Navy\n    Color::DarkGreen\n    Color::DarkRed\n    Color::Brown\n    Color::Purple\n    Color::Orange\n    Color::Pink\n    Color::Gold\n    Color::LightBlue\n    Color::LightGreen\n    Color::LightRed\n    Color::Transparent\n}\n
    "},{"location":"manual/advanced_graphics/color_palettes/#best-practices","title":"Best Practices","text":""},{"location":"manual/advanced_graphics/color_palettes/#palette-selection","title":"Palette Selection","text":"
    • Match game style: Choose palette that fits your game's theme
    • Test on hardware: Colors may look different on ESP32 display
    • Consider contrast: Ensure sprites are visible against background
    • Consistency: Stick with one palette per scene (or use dual mode)
    "},{"location":"manual/advanced_graphics/color_palettes/#dual-palette-mode_1","title":"Dual Palette Mode","text":"
    • Use sparingly: Not all games need dual palettes
    • Test combinations: Some palette combinations work better than others
    • Clear separation: Use for clear visual distinction between layers
    • Performance: No performance cost, use freely
    "},{"location":"manual/advanced_graphics/color_palettes/#custom-palettes_1","title":"Custom Palettes","text":"
    • Limit colors: Keep palette size reasonable (8-16 colors)
    • Plan ahead: Design palette before creating sprites
    • Test thoroughly: Verify colors work well together
    • Document: Comment your palette choices
    "},{"location":"manual/advanced_graphics/color_palettes/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/advanced_graphics/color_palettes/#palette-switching","title":"Palette Switching","text":"
    class GameScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Set initial palette\n        pixelroot32::graphics::setPalette(\n            pixelroot32::graphics::PaletteType::NES\n        );\n    }\n\n    void changeToNightMode() {\n        // Switch to darker palette\n        pixelroot32::graphics::setPalette(\n            pixelroot32::graphics::PaletteType::GB\n        );\n    }\n\n    void changeToDayMode() {\n        // Switch to brighter palette\n        pixelroot32::graphics::setPalette(\n            pixelroot32::graphics::PaletteType::PICO8\n        );\n    }\n};\n
    "},{"location":"manual/advanced_graphics/color_palettes/#theme-based-palettes","title":"Theme-Based Palettes","text":"
    namespace GamePalettes {\n    // Forest theme\n    static const pixelroot32::graphics::Color FOREST[] = {\n        Color::Black,\n        Color::DarkGreen,\n        Color::Green,\n        Color::LightGreen,\n        Color::Brown,\n        Color::Yellow\n    };\n\n    // Desert theme\n    static const pixelroot32::graphics::Color DESERT[] = {\n        Color::Black,\n        Color::Brown,\n        Color::Yellow,\n        Color::Gold,\n        Color::Orange,\n        Color::White\n    };\n\n    // Ocean theme\n    static const pixelroot32::graphics::Color OCEAN[] = {\n        Color::Black,\n        Color::Navy,\n        Color::Blue,\n        Color::Cyan,\n        Color::LightBlue,\n        Color::White\n    };\n}\n
    "},{"location":"manual/advanced_graphics/color_palettes/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/advanced_graphics/color_palettes/#colors-not-changing","title":"Colors Not Changing","text":"
    • Verify setPalette() is called before drawing
    • Check palette is set in init(), not update()
    • Ensure dual palette mode is enabled if using separate palettes
    • Verify Color constants are from correct namespace
    "},{"location":"manual/advanced_graphics/color_palettes/#colors-look-wrong-on-hardware","title":"Colors Look Wrong on Hardware","text":"
    • ESP32 displays may render colors differently
    • Test on actual hardware, not just PC
    • Adjust palette colors if needed
    • Consider display calibration
    "},{"location":"manual/advanced_graphics/color_palettes/#dual-palette-not-working","title":"Dual Palette Not Working","text":"
    • Ensure enableDualPaletteMode() is called first
    • Verify both palettes are set
    • Check that you're drawing in correct context
    • Review renderer documentation
    "},{"location":"manual/advanced_graphics/color_palettes/#next-steps","title":"Next Steps","text":"

    Now that you understand palettes, learn about: - Cameras and Scrolling - Create scrolling levels - Tilemaps - Build levels with tiles - Particles and Effects - Add visual effects

    See also: - API Reference - PaletteDefs - API Reference - Color - Manual - Basic Rendering

    "},{"location":"manual/advanced_graphics/particles_and_effects/","title":"Particles and Effects","text":"

    The particle system allows you to create visual effects like fire, explosions, smoke, and sparks. This guide covers ParticleEmitter, ParticleConfig, and the included presets.

    "},{"location":"manual/advanced_graphics/particles_and_effects/#particleemitter-basics","title":"ParticleEmitter Basics","text":"

    A ParticleEmitter is an Entity that manages a pool of particles to create visual effects.

    "},{"location":"manual/advanced_graphics/particles_and_effects/#creating-a-particle-emitter","title":"Creating a Particle Emitter","text":"
    #include <graphics/particles/ParticleEmitter.h>\n#include <graphics/particles/ParticleConfig.h>\n\n// Create particle configuration\npixelroot32::graphics::particles::ParticleConfig config;\nconfig.startColor = pixelroot32::graphics::Color::Red;\nconfig.endColor = pixelroot32::graphics::Color::Yellow;\nconfig.lifetime = 1.0f; // 1 second\nconfig.speed = 50.0f;\nconfig.gravity = -100.0f; // Upward (negative = up)\n\n// Create emitter\npixelroot32::graphics::particles::ParticleEmitter* emitter = \n    new pixelroot32::graphics::particles::ParticleEmitter(100, 100, config);\n\n// Add to scene\naddEntity(emitter);\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#emitting-particles","title":"Emitting Particles","text":"
    // Emit a burst of particles\nemitter->burst(100, 100, 10); // x, y, particle count\n\n// Particles will automatically update and draw\n// No additional code needed!\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#particleconfig","title":"ParticleConfig","text":"

    ParticleConfig defines how particles behave:

    #include <graphics/particles/ParticleConfig.h>\n\npixelroot32::graphics::particles::ParticleConfig config;\n\n// Colors\nconfig.startColor = pixelroot32::graphics::Color::Red;   // Color at spawn\nconfig.endColor = pixelroot32::graphics::Color::Yellow;  // Color at death\n\n// Lifetime\nconfig.lifetime = 0.5f; // Duration in seconds\n\n// Velocity\nconfig.speed = 100.0f;           // Base speed\nconfig.speedVariation = 20.0f;   // Random variation\nconfig.direction = 90.0f;        // Direction in degrees (0 = right, 90 = up)\nconfig.directionVariation = 45.0f; // Random direction spread\n\n// Physics\nconfig.gravity = 200.0f;  // Gravity force (positive = down)\nconfig.friction = 0.95f;   // Friction (0.0 to 1.0, 1.0 = no friction)\n\n// Size\nconfig.startSize = 2;     // Size at spawn (pixels)\nconfig.endSize = 1;       // Size at death\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#complete-config-example","title":"Complete Config Example","text":"
    pixelroot32::graphics::particles::ParticleConfig fireConfig;\n\n// Fire colors (red to yellow)\nfireConfig.startColor = pixelroot32::graphics::Color::Red;\nfireConfig.endColor = pixelroot32::graphics::Color::Yellow;\n\n// Short lifetime\nfireConfig.lifetime = 0.3f;\n\n// Upward movement with variation\nfireConfig.speed = 80.0f;\nfireConfig.speedVariation = 30.0f;\nfireConfig.direction = 90.0f; // Up\nfireConfig.directionVariation = 30.0f; // Spread\n\n// Upward gravity (negative)\nfireConfig.gravity = -50.0f;\n\n// Slight friction\nfireConfig.friction = 0.98f;\n\n// Size\nfireConfig.startSize = 3;\nfireConfig.endSize = 1;\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#built-in-presets","title":"Built-in Presets","text":"

    PixelRoot32 includes several particle presets for common effects:

    "},{"location":"manual/advanced_graphics/particles_and_effects/#fire","title":"Fire","text":"
    #include <graphics/particles/ParticlePresets.h>\n\n// Create fire emitter\npixelroot32::graphics::particles::ParticleEmitter* fire = \n    new pixelroot32::graphics::particles::ParticleEmitter(\n        100, 100,\n        pixelroot32::graphics::particles::ParticlePresets::Fire()\n    );\n\n// Emit continuous fire\nvoid update(unsigned long deltaTime) override {\n    fire->burst(100, 100, 2); // Emit 2 particles per frame\n    Scene::update(deltaTime);\n}\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#explosion","title":"Explosion","text":"
    // Create explosion emitter\npixelroot32::graphics::particles::ParticleEmitter* explosion = \n    new pixelroot32::graphics::particles::ParticleEmitter(\n        100, 100,\n        pixelroot32::graphics::particles::ParticlePresets::Explosion()\n    );\n\n// Emit explosion burst\nexplosion->burst(100, 100, 20); // 20 particles at once\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#sparks","title":"Sparks","text":"
    // Create sparks emitter\npixelroot32::graphics::particles::ParticleEmitter* sparks = \n    new pixelroot32::graphics::particles::ParticleEmitter(\n        100, 100,\n        pixelroot32::graphics::particles::ParticlePresets::Sparks()\n    );\n\n// Emit sparks\nsparks->burst(100, 100, 10);\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#smoke","title":"Smoke","text":"
    // Create smoke emitter\npixelroot32::graphics::particles::ParticleEmitter* smoke = \n    new pixelroot32::graphics::particles::ParticleEmitter(\n        100, 100,\n        pixelroot32::graphics::particles::ParticlePresets::Smoke()\n    );\n\n// Emit smoke\nsmoke->burst(100, 100, 3);\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#dust","title":"Dust","text":"
    // Create dust emitter\npixelroot32::graphics::particles::ParticleEmitter* dust = \n    new pixelroot32::graphics::particles::ParticleEmitter(\n        100, 100,\n        pixelroot32::graphics::particles::ParticlePresets::Dust()\n    );\n\n// Emit dust\ndust->burst(100, 100, 5);\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#complete-example-explosion-effect","title":"Complete Example: Explosion Effect","text":"
    #include <core/Scene.h>\n#include <graphics/particles/ParticleEmitter.h>\n#include <graphics/particles/ParticlePresets.h>\n\nclass ExplosionEffect : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::particles::ParticleEmitter* explosion;\n    bool active = false;\n\npublic:\n    ExplosionEffect()\n        : Entity(0, 0, 1, 1, pixelroot32::core::EntityType::GENERIC) {\n        setRenderLayer(1);\n\n        // Create explosion emitter\n        explosion = new pixelroot32::graphics::particles::ParticleEmitter(\n            0, 0,\n            pixelroot32::graphics::particles::ParticlePresets::Explosion()\n        );\n    }\n\n    void trigger(float x, float y) {\n        active = true;\n        this->x = x;\n        this->y = y;\n\n        // Emit explosion burst\n        explosion->burst(x, y, 25);\n    }\n\n    void update(unsigned long deltaTime) override {\n        explosion->update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        explosion->draw(renderer);\n    }\n};\n\n// Usage in scene\nvoid MyScene::init() override {\n    explosionEffect = new ExplosionEffect();\n    addEntity(explosionEffect);\n}\n\nvoid MyScene::update(unsigned long deltaTime) override {\n    auto& input = engine.getInputManager();\n\n    // Trigger explosion on button press\n    if (input.isButtonPressed(4)) { // Button A\n        explosionEffect->trigger(player->x, player->y);\n    }\n\n    Scene::update(deltaTime);\n}\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#continuous-effects","title":"Continuous Effects","text":"

    For continuous effects like fire or smoke:

    class FireEffect : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::particles::ParticleEmitter* fire;\n    unsigned long emitTimer = 0;\n    const unsigned long EMIT_INTERVAL_MS = 50; // Emit every 50ms\n\npublic:\n    FireEffect(float x, float y)\n        : Entity(x, y, 1, 1, pixelroot32::core::EntityType::GENERIC) {\n        setRenderLayer(1);\n\n        fire = new pixelroot32::graphics::particles::ParticleEmitter(\n            x, y,\n            pixelroot32::graphics::particles::ParticlePresets::Fire()\n        );\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Emit particles continuously\n        emitTimer += deltaTime;\n        if (emitTimer >= EMIT_INTERVAL_MS) {\n            emitTimer -= EMIT_INTERVAL_MS;\n            fire->burst(x, y, 2); // 2 particles per interval\n        }\n\n        fire->update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        fire->draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#custom-particle-effects","title":"Custom Particle Effects","text":"

    Create your own particle effects by customizing ParticleConfig:

    "},{"location":"manual/advanced_graphics/particles_and_effects/#magic-spell-effect","title":"Magic Spell Effect","text":"
    pixelroot32::graphics::particles::ParticleConfig magicConfig;\n\n// Magical colors (purple to cyan)\nmagicConfig.startColor = pixelroot32::graphics::Color::Purple;\nmagicConfig.endColor = pixelroot32::graphics::Color::Cyan;\n\n// Medium lifetime\nmagicConfig.lifetime = 0.8f;\n\n// Outward spread\nmagicConfig.speed = 60.0f;\nmagicConfig.speedVariation = 20.0f;\nmagicConfig.direction = 0.0f; // Right\nmagicConfig.directionVariation = 360.0f; // Full circle\n\n// Slight upward float\nmagicConfig.gravity = -30.0f;\n\n// Low friction (floaty)\nmagicConfig.friction = 0.92f;\n\n// Size\nmagicConfig.startSize = 2;\nmagicConfig.endSize = 1;\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#rain-effect","title":"Rain Effect","text":"
    pixelroot32::graphics::particles::ParticleConfig rainConfig;\n\n// Rain color (light blue)\nrainConfig.startColor = pixelroot32::graphics::Color::LightBlue;\nrainConfig.endColor = pixelroot32::graphics::Color::LightBlue;\n\n// Long lifetime\nrainConfig.lifetime = 2.0f;\n\n// Downward movement\nrainConfig.speed = 150.0f;\nrainConfig.speedVariation = 20.0f;\nrainConfig.direction = 270.0f; // Down\nrainConfig.directionVariation = 5.0f; // Slight angle variation\n\n// Downward gravity\nrainConfig.gravity = 200.0f;\n\n// No friction\nrainConfig.friction = 1.0f;\n\n// Small size\nrainConfig.startSize = 1;\nrainConfig.endSize = 1;\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#best-practices","title":"Best Practices","text":""},{"location":"manual/advanced_graphics/particles_and_effects/#performance","title":"Performance","text":"
    • Limit particle count: Each emitter has MAX_PARTICLES_PER_EMITTER (50)
    • Reuse emitters: Don't create new emitters every frame
    • Disable when not visible: Set isVisible = false when off-screen
    • Limit active emitters: Too many emitters can impact performance
    "},{"location":"manual/advanced_graphics/particles_and_effects/#visual-design","title":"Visual Design","text":"
    • Match game style: Particle effects should fit your game's aesthetic
    • Use appropriate colors: Match particle colors to game palette
    • Test on hardware: ESP32 may render particles differently
    • Keep it simple: Simple effects often look better than complex ones
    "},{"location":"manual/advanced_graphics/particles_and_effects/#timing","title":"Timing","text":"
    • Burst timing: Space out bursts for better visual effect
    • Continuous effects: Use timers to control emission rate
    • Lifetime: Adjust lifetime to match effect duration
    • Cleanup: Particles automatically clean up when lifetime expires
    "},{"location":"manual/advanced_graphics/particles_and_effects/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/advanced_graphics/particles_and_effects/#one-shot-effect","title":"One-Shot Effect","text":"
    class OneShotEffect : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::particles::ParticleEmitter* emitter;\n    bool hasEmitted = false;\n\npublic:\n    void trigger(float x, float y) {\n        if (!hasEmitted) {\n            emitter->burst(x, y, 20);\n            hasEmitted = true;\n        }\n    }\n\n    void reset() {\n        hasEmitted = false;\n    }\n};\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#attached-effect","title":"Attached Effect","text":"
    class AttachedParticleEffect : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::particles::ParticleEmitter* emitter;\n    pixelroot32::core::Actor* target;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        // Update emitter position to follow target\n        emitter->x = target->x;\n        emitter->y = target->y;\n\n        // Emit particles\n        emitter->burst(target->x, target->y, 1);\n\n        emitter->update(deltaTime);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/advanced_graphics/particles_and_effects/#particles-not-appearing","title":"Particles Not Appearing","text":"
    • Verify emitter is added to scene
    • Check particle config is valid
    • Ensure burst() is being called
    • Verify emitter position is on-screen
    "},{"location":"manual/advanced_graphics/particles_and_effects/#performance-issues","title":"Performance Issues","text":"
    • Reduce particle count per burst
    • Limit number of active emitters
    • Use simpler particle configs
    • Disable emitters when not visible
    "},{"location":"manual/advanced_graphics/particles_and_effects/#particles-not-moving","title":"Particles Not Moving","text":"
    • Check gravity value (positive = down, negative = up)
    • Verify speed is not 0
    • Check friction isn't too high (1.0 = no movement)
    • Ensure direction is correct (degrees: 0=right, 90=up, 180=left, 270=down)
    "},{"location":"manual/advanced_graphics/particles_and_effects/#next-steps","title":"Next Steps","text":"

    Now that you understand particles, you've completed the advanced graphics section. Continue with: - Performance Optimization - Optimize your game - Memory Management - Manage memory efficiently - API Reference - Complete API documentation

    See also: - API Reference - ParticleEmitter - API Reference - ParticleConfig - API Reference - ParticlePresets - Manual - Basic Rendering

    "},{"location":"manual/advanced_graphics/sprites_and_animation/","title":"Sprites and Animation","text":"

    This guide covers advanced sprite techniques and animation in PixelRoot32, including different sprite formats, creating animations, and best practices.

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#sprite-formats","title":"Sprite Formats","text":"

    PixelRoot32 supports multiple sprite formats, each optimized for different use cases.

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#1bpp-standard-monochrome","title":"1bpp (Standard, Monochrome)","text":"

    The standard format uses 1 bit per pixel (monochrome). This is the most memory-efficient format:

    #include <graphics/Renderer.h>\n\n// Define sprite data (8x8 example)\nstatic const uint16_t PLAYER_SPRITE_DATA[] = {\n    0b00111100,  // Row 0\n    0b01111110,  // Row 1\n    0b11111111,  // Row 2\n    0b11111111,  // Row 3\n    0b11111111,  // Row 4\n    0b01111110,  // Row 5\n    0b00111100,  // Row 6\n    0b00000000   // Row 7\n};\n\n// Create sprite descriptor\nstatic const pixelroot32::graphics::Sprite PLAYER_SPRITE = {\n    PLAYER_SPRITE_DATA,\n    8,  // width\n    8   // height\n};\n\n// Draw sprite\nrenderer.drawSprite(PLAYER_SPRITE, 100, 100, pixelroot32::graphics::Color::White);\n

    Characteristics: - Most memory-efficient - 1 bit per pixel - Maximum width: 16 pixels - Color applied at draw time - Best for: Simple graphics, retro style

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#2bpp-experimental-4-colors","title":"2bpp (Experimental, 4 Colors)","text":"

    2 bits per pixel allows 4 colors per sprite:

    #ifdef PIXELROOT32_ENABLE_2BPP_SPRITES\n#include <graphics/Renderer.h>\n\n// Define 2bpp sprite data\nstatic const uint8_t COLORFUL_SPRITE_DATA[] = {\n    // Each byte represents 4 pixels (2 bits each)\n    // Format: [pixel3][pixel2][pixel1][pixel0]\n    0x00, 0x11, 0x22, 0x33,  // Row 0\n    0x11, 0x22, 0x33, 0x00,  // Row 1\n    // ... more rows\n};\n\n// Define palette (4 colors)\nstatic const pixelroot32::graphics::Color SPRITE_PALETTE[] = {\n    pixelroot32::graphics::Color::Transparent,\n    pixelroot32::graphics::Color::Red,\n    pixelroot32::graphics::Color::Green,\n    pixelroot32::graphics::Color::Blue\n};\n\n// Create 2bpp sprite\nstatic const pixelroot32::graphics::Sprite2bpp COLORFUL_SPRITE = {\n    COLORFUL_SPRITE_DATA,\n    SPRITE_PALETTE,\n    16,  // width\n    8,   // height\n    4    // palette size\n};\n\n// Draw 2bpp sprite\nrenderer.drawSprite(COLORFUL_SPRITE, 100, 100, false);\n#endif\n

    Characteristics: - 2 bits per pixel (4 colors) - Requires custom palette - More memory than 1bpp - Best for: More colorful sprites without full color

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#4bpp-experimental-16-colors","title":"4bpp (Experimental, 16 Colors)","text":"

    4 bits per pixel allows 16 colors per sprite:

    #ifdef PIXELROOT32_ENABLE_4BPP_SPRITES\n#include <graphics/Renderer.h>\n\n// Define 4bpp sprite data\nstatic const uint8_t RICH_SPRITE_DATA[] = {\n    // Each byte represents 2 pixels (4 bits each)\n    // Format: [pixel1][pixel0]\n    0x01, 0x23, 0x45, 0x67,  // Row 0\n    // ... more rows\n};\n\n// Define palette (16 colors)\nstatic const pixelroot32::graphics::Color RICH_PALETTE[] = {\n    pixelroot32::graphics::Color::Transparent,\n    pixelroot32::graphics::Color::Black,\n    pixelroot32::graphics::Color::DarkGray,\n    // ... 13 more colors\n};\n\n// Create 4bpp sprite\nstatic const pixelroot32::graphics::Sprite4bpp RICH_SPRITE = {\n    RICH_SPRITE_DATA,\n    RICH_PALETTE,\n    16,  // width\n    16,  // height\n    16   // palette size\n};\n\n// Draw 4bpp sprite\nrenderer.drawSprite(RICH_SPRITE, 100, 100, false);\n#endif\n

    Characteristics: - 4 bits per pixel (16 colors) - Requires custom palette - Most memory-intensive - Best for: Detailed sprites with many colors

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#multisprite-multi-layer","title":"MultiSprite (Multi-Layer)","text":"

    MultiSprite combines multiple 1bpp layers to create multi-color sprites:

    #include <graphics/Renderer.h>\n\n// Define layers (each is 1bpp)\nstatic const uint16_t BASE_LAYER_DATA[] = {\n    0b00111100,\n    0b01111110,\n    0b11111111,\n    0b11111111,\n    0b11111111,\n    0b01111110,\n    0b00111100,\n    0b00000000\n};\n\nstatic const uint16_t HIGHLIGHT_LAYER_DATA[] = {\n    0b00000000,\n    0b00011000,\n    0b00111100,\n    0b00111100,\n    0b00111100,\n    0b00011000,\n    0b00000000,\n    0b00000000\n};\n\n// Create layers\nstatic const pixelroot32::graphics::SpriteLayer LAYERS[] = {\n    { BASE_LAYER_DATA, pixelroot32::graphics::Color::Blue },      // Base layer\n    { HIGHLIGHT_LAYER_DATA, pixelroot32::graphics::Color::Cyan }  // Highlight layer\n};\n\n// Create MultiSprite\nstatic const pixelroot32::graphics::MultiSprite PLAYER_MULTI = {\n    8,      // width\n    8,      // height\n    LAYERS, // layers array\n    2       // layer count\n};\n\n// Draw MultiSprite\nrenderer.drawSprite(PLAYER_MULTI, 100, 100, false);\n

    Characteristics: - Combines multiple 1bpp layers - Each layer can have different color - Layers drawn in order (first = bottom) - Best for: Complex sprites with highlights, outlines, etc.

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#creating-sprites","title":"Creating Sprites","text":""},{"location":"manual/advanced_graphics/sprites_and_animation/#manual-creation-1bpp","title":"Manual Creation (1bpp)","text":"

    For simple sprites, you can create them manually:

    // 8x8 sprite: Simple circle\nstatic const uint16_t CIRCLE_SPRITE_DATA[] = {\n    0b00111100,  //   ####\n    0b01111110,  //  ######\n    0b11111111,  // ########\n    0b11111111,  // ########\n    0b11111111,  // ########\n    0b11111111,  // ########\n    0b01111110,  //  ######\n    0b00111100   //   ####\n};\n\nstatic const pixelroot32::graphics::Sprite CIRCLE_SPRITE = {\n    CIRCLE_SPRITE_DATA,\n    8,\n    8\n};\n

    Tips: - Use binary notation for clarity - Comment each row to visualize - Keep sprites small (8x8, 16x16) - Reuse sprites when possible

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#using-sprite-compiler","title":"Using Sprite Compiler","text":"

    For complex sprites, use the Sprite Compiler tool (if available) to convert PNG images to sprite data.

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#sprite-animation","title":"Sprite Animation","text":"

    PixelRoot32 uses a step-based animation system that's lightweight and efficient.

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#spriteanimation-structure","title":"SpriteAnimation Structure","text":"
    #include <graphics/Renderer.h>\n\n// Define animation frames\nstatic const uint16_t FRAME1_DATA[] = { /* ... */ };\nstatic const uint16_t FRAME2_DATA[] = { /* ... */ };\nstatic const uint16_t FRAME3_DATA[] = { /* ... */ };\n\nstatic const pixelroot32::graphics::Sprite FRAME1 = { FRAME1_DATA, 8, 8 };\nstatic const pixelroot32::graphics::Sprite FRAME2 = { FRAME2_DATA, 8, 8 };\nstatic const pixelroot32::graphics::Sprite FRAME3 = { FRAME3_DATA, 8, 8 };\n\n// Create animation frames\nstatic const pixelroot32::graphics::SpriteAnimationFrame ANIMATION_FRAMES[] = {\n    { &FRAME1, nullptr },  // Frame 1 (no mask)\n    { &FRAME2, nullptr },  // Frame 2\n    { &FRAME3, nullptr }   // Frame 3\n};\n\n// Create animation\npixelroot32::graphics::SpriteAnimation walkAnimation;\nwalkAnimation.frames = ANIMATION_FRAMES;\nwalkAnimation.frameCount = 3;\nwalkAnimation.current = 0;\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#using-animations-in-actors","title":"Using Animations in Actors","text":"
    #include <core/Actor.h>\n#include <graphics/Renderer.h>\n\nclass AnimatedPlayer : public pixelroot32::core::Actor {\nprivate:\n    pixelroot32::graphics::SpriteAnimation walkAnimation;\n    unsigned long animationTimer = 0;\n    const unsigned long FRAME_DURATION_MS = 100; // 100ms per frame\n\npublic:\n    AnimatedPlayer(float x, float y)\n        : Actor(x, y, 8, 8) {\n        setRenderLayer(1);\n\n        // Initialize animation\n        walkAnimation.frames = WALK_ANIMATION_FRAMES;\n        walkAnimation.frameCount = 3;\n        walkAnimation.current = 0;\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Update animation\n        animationTimer += deltaTime;\n        if (animationTimer >= FRAME_DURATION_MS) {\n            animationTimer -= FRAME_DURATION_MS;\n            walkAnimation.step(); // Advance to next frame\n        }\n\n        // Movement logic...\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Get current frame\n        const pixelroot32::graphics::Sprite* currentFrame = \n            walkAnimation.frames[walkAnimation.current].sprite;\n\n        // Draw current frame\n        renderer.drawSprite(\n            *currentFrame,\n            static_cast<int>(x),\n            static_cast<int>(y),\n            pixelroot32::graphics::Color::White,\n            false // flipX\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Handle collision\n    }\n};\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#animation-control","title":"Animation Control","text":"
    // Reset animation to first frame\nwalkAnimation.reset();\n\n// Step to next frame (loops automatically)\nwalkAnimation.step();\n\n// Get current frame\nconst pixelroot32::graphics::Sprite* frame = \n    walkAnimation.frames[walkAnimation.current].sprite;\n\n// Check if animation is at specific frame\nif (walkAnimation.current == 0) {\n    // At first frame\n}\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#multiple-animations","title":"Multiple Animations","text":"

    For actors with multiple animations (idle, walk, jump):

    class PlayerActor : public pixelroot32::core::Actor {\nprivate:\n    enum class AnimationState {\n        IDLE,\n        WALK,\n        JUMP\n    };\n\n    AnimationState currentState = AnimationState::IDLE;\n    pixelroot32::graphics::SpriteAnimation idleAnim;\n    pixelroot32::graphics::SpriteAnimation walkAnim;\n    pixelroot32::graphics::SpriteAnimation jumpAnim;\n\n    pixelroot32::graphics::SpriteAnimation* getCurrentAnimation() {\n        switch (currentState) {\n            case AnimationState::IDLE: return &idleAnim;\n            case AnimationState::WALK: return &walkAnim;\n            case AnimationState::JUMP: return &jumpAnim;\n        }\n        return &idleAnim;\n    }\n\npublic:\n    void update(unsigned long deltaTime) override {\n        // Update current animation\n        auto* anim = getCurrentAnimation();\n        animationTimer += deltaTime;\n        if (animationTimer >= FRAME_DURATION_MS) {\n            animationTimer -= FRAME_DURATION_MS;\n            anim->step();\n        }\n\n        // Change animation state based on game logic\n        if (isMoving) {\n            if (currentState != AnimationState::WALK) {\n                currentState = AnimationState::WALK;\n                walkAnim.reset();\n            }\n        } else if (isJumping) {\n            if (currentState != AnimationState::JUMP) {\n                currentState = AnimationState::JUMP;\n                jumpAnim.reset();\n            }\n        } else {\n            if (currentState != AnimationState::IDLE) {\n                currentState = AnimationState::IDLE;\n                idleAnim.reset();\n            }\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        auto* anim = getCurrentAnimation();\n        const auto* frame = anim->frames[anim->current].sprite;\n        renderer.drawSprite(*frame, static_cast<int>(x), static_cast<int>(y), \n            pixelroot32::graphics::Color::White);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#sprite-flipping","title":"Sprite Flipping","text":"

    Flip sprites horizontally for facing direction:

    bool facingRight = true;\n\nvoid draw(pixelroot32::graphics::Renderer& renderer) override {\n    renderer.drawSprite(\n        PLAYER_SPRITE,\n        static_cast<int>(x),\n        static_cast<int>(y),\n        pixelroot32::graphics::Color::White,\n        !facingRight // Flip if facing left\n    );\n}\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#best-practices","title":"Best Practices","text":""},{"location":"manual/advanced_graphics/sprites_and_animation/#memory-optimization","title":"Memory Optimization","text":"
    • Reuse sprites: Define sprites once, use many times
    • Use 1bpp when possible: Most memory-efficient
    • Store in flash: Use static const to keep in flash memory
    • Limit sprite count: Too many unique sprites can exhaust memory
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#performance","title":"Performance","text":"
    • Pre-calculate animations: Set up animations in init(), not update()
    • Limit active animations: Only animate visible entities
    • Use appropriate formats: Don't use 4bpp if 1bpp works
    • Batch similar sprites: Draw similar sprites together
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#organization","title":"Organization","text":"
    • Group related sprites: Keep sprite data together
    • Use meaningful names: Name sprites clearly
    • Document complex sprites: Comment sprite bit patterns
    • Create sprite libraries: Reusable sprite collections
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/advanced_graphics/sprites_and_animation/#sprite-sheet-pattern","title":"Sprite Sheet Pattern","text":"

    Organize multiple sprites in arrays:

    namespace PlayerSprites {\n    static const uint16_t IDLE_FRAME1[] = { /* ... */ };\n    static const uint16_t IDLE_FRAME2[] = { /* ... */ };\n    static const uint16_t WALK_FRAME1[] = { /* ... */ };\n    static const uint16_t WALK_FRAME2[] = { /* ... */ };\n\n    static const pixelroot32::graphics::Sprite IDLE[] = {\n        { IDLE_FRAME1, 8, 8 },\n        { IDLE_FRAME2, 8, 8 }\n    };\n\n    static const pixelroot32::graphics::Sprite WALK[] = {\n        { WALK_FRAME1, 8, 8 },\n        { WALK_FRAME2, 8, 8 }\n    };\n}\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#animation-helper","title":"Animation Helper","text":"

    Create a helper class for animation management:

    class AnimationController {\nprivate:\n    pixelroot32::graphics::SpriteAnimation* currentAnim;\n    unsigned long timer = 0;\n    unsigned long frameDuration;\n\npublic:\n    void setAnimation(pixelroot32::graphics::SpriteAnimation* anim) {\n        if (currentAnim != anim) {\n            currentAnim = anim;\n            currentAnim->reset();\n            timer = 0;\n        }\n    }\n\n    void update(unsigned long deltaTime) {\n        if (currentAnim) {\n            timer += deltaTime;\n            if (timer >= frameDuration) {\n                timer -= frameDuration;\n                currentAnim->step();\n            }\n        }\n    }\n\n    const pixelroot32::graphics::Sprite* getCurrentFrame() {\n        if (currentAnim && currentAnim->frameCount > 0) {\n            return currentAnim->frames[currentAnim->current].sprite;\n        }\n        return nullptr;\n    }\n};\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/advanced_graphics/sprites_and_animation/#sprites-not-displaying","title":"Sprites Not Displaying","text":"
    • Check sprite data is valid
    • Verify width/height match data
    • Ensure sprite is within screen bounds
    • Check render layer is correct
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#animation-not-playing","title":"Animation Not Playing","text":"
    • Verify animation frames are set
    • Check step() is being called
    • Ensure timer logic is correct
    • Verify frame count matches array size
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#memory-issues","title":"Memory Issues","text":"
    • Reduce sprite count
    • Use 1bpp instead of 2bpp/4bpp
    • Reuse sprites more
    • Check available flash memory
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#next-steps","title":"Next Steps","text":"

    Now that you understand sprites and animation, learn about: - Color Palettes - Use different color schemes - Cameras and Scrolling - Create scrolling levels - Tilemaps - Build levels with tiles

    See also: - API Reference - Sprite - API Reference - SpriteAnimation - Manual - Basic Rendering

    "},{"location":"manual/advanced_graphics/tilemaps/","title":"Tilemaps","text":"

    Tilemaps allow you to build levels efficiently by reusing small tile sprites. This guide covers creating tilemaps, rendering them, and using them with scrolling cameras.

    "},{"location":"manual/advanced_graphics/tilemaps/#what-are-tilemaps","title":"What are Tilemaps?","text":"

    A tilemap is a 2D grid where each cell references a tile sprite. Instead of placing individual sprites, you define which tile appears at each grid position.

    Advantages: - Memory efficient: Reuse tile sprites many times - Easy level design: Edit level data, not code - Fast rendering: Optimized tilemap drawing - Large levels: Create levels bigger than screen - Multiple Bit-Depths: Support for 1bpp, 2bpp, and 4bpp tilemaps for higher graphical fidelity

    "},{"location":"manual/advanced_graphics/tilemaps/#creating-a-tilemap","title":"Creating a Tilemap","text":""},{"location":"manual/advanced_graphics/tilemaps/#1-define-tiles","title":"1. Define Tiles","text":"

    First, create the tile sprites you'll reuse. You can use standard 1bpp sprites or multi-bpp sprites (2bpp/4bpp) if enabled.

    "},{"location":"manual/advanced_graphics/tilemaps/#1bpp-tiles-example","title":"1bpp Tiles Example","text":"
    #include <graphics/Renderer.h>\n\n// Ground tile (solid)\nstatic const uint16_t TILE_GROUND_BITS[] = {\n    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,\n    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF\n};\n\n// Create tile sprites (8x8 tiles)\nstatic const pixelroot32::graphics::Sprite TILES[] = {\n    { TILE_EMPTY_BITS, 8, 8 },  // Index 0: Empty\n    { TILE_GROUND_BITS, 8, 8 }  // Index 1: Ground\n};\n
    "},{"location":"manual/advanced_graphics/tilemaps/#2bpp-tiles-example-multi-color","title":"2bpp Tiles Example (Multi-color)","text":"
    #include <graphics/Renderer.h>\n\n// 2bpp grass tile\nstatic const uint8_t TILE_GRASS_DATA[] = {\n    0b01010101, 0b01010101, // Packed 2bpp data\n    // ...\n};\n\nstatic const Color GRASS_PALETTE[] = {\n    Color::Transparent, Color::DarkGreen, Color::Green, Color::LightGreen\n};\n\nstatic const pixelroot32::graphics::Sprite2bpp TILES_2BPP[] = {\n    { TILE_GRASS_DATA, GRASS_PALETTE, 8, 8, 4 }\n};\n
    "},{"location":"manual/advanced_graphics/tilemaps/#2-create-tile-index-array","title":"2. Create Tile Index Array","text":"

    Define which tile appears at each position:

    // Tilemap dimensions (30 tiles wide, 20 tiles tall)\nstatic const int TILEMAP_WIDTH = 30;\nstatic const int TILEMAP_HEIGHT = 20;\n\n// Array of tile indices (each byte is a tile index)\nstatic uint8_t TILEMAP_INDICES[TILEMAP_WIDTH * TILEMAP_HEIGHT];\n\n// Initialize to empty\nvoid initTilemap() {\n    for (int i = 0; i < TILEMAP_WIDTH * TILEMAP_HEIGHT; i++) {\n        TILEMAP_INDICES[i] = 0; // Empty\n    }\n\n    // Draw ground at bottom\n    int groundRow = TILEMAP_HEIGHT - 1;\n    for (int x = 0; x < TILEMAP_WIDTH; x++) {\n        TILEMAP_INDICES[groundRow * TILEMAP_WIDTH + x] = 1; // Ground tile\n    }\n\n    // Add some walls\n    TILEMAP_INDICES[5 * TILEMAP_WIDTH + 10] = 2; // Wall at (10, 5)\n    TILEMAP_INDICES[5 * TILEMAP_WIDTH + 11] = 2;\n    TILEMAP_INDICES[5 * TILEMAP_WIDTH + 12] = 2;\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#3-create-tilemap-structure","title":"3. Create TileMap Structure","text":"
    #include <graphics/Renderer.h>\n\nstatic pixelroot32::graphics::TileMap myTileMap = {\n    TILEMAP_INDICES,                    // indices array\n    TILEMAP_WIDTH,                      // width (in tiles)\n    TILEMAP_HEIGHT,                     // height (in tiles)\n    TILES,                              // tiles array\n    8,                                  // tile width (pixels)\n    8,                                  // tile height (pixels)\n    sizeof(TILES) / sizeof(pixelroot32::graphics::Sprite) // tile count\n};\n
    "},{"location":"manual/advanced_graphics/tilemaps/#rendering-tilemaps","title":"Rendering Tilemaps","text":""},{"location":"manual/advanced_graphics/tilemaps/#basic-rendering","title":"Basic Rendering","text":"
    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // 1bpp Tilemap (requires a color)\n    renderer.drawTileMap(\n        myTileMap,\n        0, 0,\n        pixelroot32::graphics::Color::White\n    );\n\n    // 2bpp/4bpp Tilemap (colors are in the sprite palettes)\n    renderer.drawTileMap(myTileMap2bpp, 0, 100);\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#with-camerascrolling","title":"With Camera/Scrolling","text":"
    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Apply camera first\n    camera.apply(renderer);\n\n    // Draw tilemap (camera offset is automatically applied)\n    renderer.drawTileMap(\n        myTileMap,\n        0,                              // World position (0, 0)\n        0,\n        pixelroot32::graphics::Color::White\n    );\n\n    // Draw game objects\n    Scene::draw(renderer);\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#complete-example-platformer-level","title":"Complete Example: Platformer Level","text":"
    #include <core/Scene.h>\n#include <graphics/Renderer.h>\n#include <graphics/Camera2D.h>\n\nclass PlatformerLevel : public pixelroot32::core::Scene {\nprivate:\n    static const int TILE_SIZE = 8;\n    static const int TILEMAP_WIDTH = 100;  // 800 pixels wide\n    static const int TILEMAP_HEIGHT = 30;   // 240 pixels tall\n\n    // Tile definitions\n    static const uint16_t TILE_EMPTY_BITS[] = {\n        0x0000, 0x0000, 0x0000, 0x0000,\n        0x0000, 0x0000, 0x0000, 0x0000\n    };\n\n    static const uint16_t TILE_GROUND_BITS[] = {\n        0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,\n        0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF\n    };\n\n    static const uint16_t TILE_GRASS_BITS[] = {\n        0x0000, 0x0000, 0x0000, 0x0000,\n        0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF\n    };\n\n    static const pixelroot32::graphics::Sprite TILES[] = {\n        { TILE_EMPTY_BITS, TILE_SIZE, TILE_SIZE },  // 0: Empty\n        { TILE_GROUND_BITS, TILE_SIZE, TILE_SIZE }, // 1: Ground\n        { TILE_GRASS_BITS, TILE_SIZE, TILE_SIZE }   // 2: Grass top\n    };\n\n    static uint8_t LEVEL_INDICES[TILEMAP_WIDTH * TILEMAP_HEIGHT];\n\n    pixelroot32::graphics::TileMap levelTileMap;\n    pixelroot32::graphics::Camera2D camera;\n\npublic:\n    PlatformerLevel() \n        : camera(240, 240) {\n        // Initialize tilemap structure\n        levelTileMap = {\n            LEVEL_INDICES,\n            TILEMAP_WIDTH,\n            TILEMAP_HEIGHT,\n            TILES,\n            TILE_SIZE,\n            TILE_SIZE,\n            sizeof(TILES) / sizeof(pixelroot32::graphics::Sprite)\n        };\n    }\n\n    void init() override {\n        // Initialize all tiles to empty\n        for (int i = 0; i < TILEMAP_WIDTH * TILEMAP_HEIGHT; i++) {\n            LEVEL_INDICES[i] = 0;\n        }\n\n        // Create ground level\n        int groundY = TILEMAP_HEIGHT - 1;\n        for (int x = 0; x < TILEMAP_WIDTH; x++) {\n            LEVEL_INDICES[groundY * TILEMAP_WIDTH + x] = 1; // Ground\n        }\n\n        // Add grass on top of ground\n        int grassY = groundY - 1;\n        for (int x = 0; x < TILEMAP_WIDTH; x++) {\n            LEVEL_INDICES[grassY * TILEMAP_WIDTH + x] = 2; // Grass\n        }\n\n        // Add platforms\n        // Platform 1: x=10 to x=15, y=20\n        for (int x = 10; x < 16; x++) {\n            LEVEL_INDICES[20 * TILEMAP_WIDTH + x] = 1; // Ground tile\n        }\n\n        // Platform 2: x=30 to x=35, y=15\n        for (int x = 30; x < 36; x++) {\n            LEVEL_INDICES[15 * TILEMAP_WIDTH + x] = 1;\n        }\n\n        // Set camera boundaries\n        camera.setBounds(0, TILEMAP_WIDTH * TILE_SIZE - 240);\n        camera.setVerticalBounds(0, TILEMAP_HEIGHT * TILE_SIZE - 240);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Follow player (example)\n        // camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Apply camera\n        camera.apply(renderer);\n\n        // Draw tilemap\n        renderer.drawTileMap(\n            levelTileMap,\n            0, 0,\n            pixelroot32::graphics::Color::White\n        );\n\n        // Draw game objects\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/tilemaps/#tilemap-with-scroll","title":"Tilemap with Scroll","text":"

    For scrolling levels, combine tilemaps with cameras:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Apply camera (handles scrolling)\n    camera.apply(renderer);\n\n    // Draw tilemap (automatically scrolled by camera)\n    renderer.drawTileMap(\n        levelTileMap,\n        0, 0,\n        pixelroot32::graphics::Color::White\n    );\n\n    // Draw entities (also scrolled)\n    Scene::draw(renderer);\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#optimizing-tilemap-rendering","title":"Optimizing Tilemap Rendering","text":""},{"location":"manual/advanced_graphics/tilemaps/#viewport-culling","title":"Viewport Culling","text":"

    Only draw visible tiles:

    void drawTileMapOptimized(\n    pixelroot32::graphics::Renderer& renderer,\n    const pixelroot32::graphics::TileMap& tileMap,\n    int offsetX, int offsetY,\n    pixelroot32::graphics::Color color\n) {\n    int screenWidth = renderer.getWidth();\n    int screenHeight = renderer.getHeight();\n\n    // Calculate which tiles are visible\n    int startTileX = (offsetX < 0) ? (-offsetX / tileMap.tileWidth) : 0;\n    int startTileY = (offsetY < 0) ? (-offsetY / tileMap.tileHeight) : 0;\n    int endTileX = startTileX + (screenWidth / tileMap.tileWidth) + 1;\n    int endTileY = startTileY + (screenHeight / tileMap.tileHeight) + 1;\n\n    // Clamp to tilemap bounds\n    if (startTileX < 0) startTileX = 0;\n    if (startTileY < 0) startTileY = 0;\n    if (endTileX > tileMap.width) endTileX = tileMap.width;\n    if (endTileY > tileMap.height) endTileY = tileMap.height;\n\n    // Draw only visible tiles\n    for (int ty = startTileY; ty < endTileY; ty++) {\n        for (int tx = startTileX; tx < endTileX; tx++) {\n            uint8_t tileIndex = tileMap.indices[ty * tileMap.width + tx];\n            if (tileIndex < tileMap.tileCount) {\n                int x = tx * tileMap.tileWidth + offsetX;\n                int y = ty * tileMap.tileHeight + offsetY;\n                renderer.drawSprite(\n                    tileMap.tiles[tileIndex],\n                    x, y,\n                    color,\n                    false\n                );\n            }\n        }\n    }\n}\n

    Note: The built-in drawTileMap() already performs viewport culling, so you typically don't need to implement this yourself.

    "},{"location":"manual/advanced_graphics/tilemaps/#best-practices","title":"Best Practices","text":""},{"location":"manual/advanced_graphics/tilemaps/#tile-design","title":"Tile Design","text":"
    • Keep tiles small: 8x8 or 16x16 pixels work best
    • Reuse tiles: Design tiles that can be used in multiple ways
    • Consistent style: All tiles should match visually
    • Limit tile count: Too many unique tiles uses more memory
    "},{"location":"manual/advanced_graphics/tilemaps/#level-design","title":"Level Design","text":"
    • Use indices efficiently: 0 = empty, 1+ = different tiles
    • Plan layout: Design level on paper/grid first
    • Test on hardware: Large tilemaps may impact performance
    • Optimize data: Use compact level data format
    "},{"location":"manual/advanced_graphics/tilemaps/#performance","title":"Performance","text":"
    • Limit tilemap size: Very large tilemaps can be slow
    • Use appropriate tile size: Smaller tiles = more tiles to draw
    • Combine with culling: Only draw visible area
    • Test scrolling: Ensure smooth scrolling performance
    "},{"location":"manual/advanced_graphics/tilemaps/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/advanced_graphics/tilemaps/#level-data-in-code","title":"Level Data in Code","text":"
    // Define level as 2D array (easier to read)\nstatic const uint8_t LEVEL_DATA[][TILEMAP_WIDTH] = {\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, // Ground\n};\n\n// Copy to tilemap indices\nvoid loadLevel() {\n    for (int y = 0; y < TILEMAP_HEIGHT; y++) {\n        for (int x = 0; x < TILEMAP_WIDTH; x++) {\n            TILEMAP_INDICES[y * TILEMAP_WIDTH + x] = LEVEL_DATA[y][x];\n        }\n    }\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#collision-detection-with-tilemaps","title":"Collision Detection with Tilemaps","text":"
    bool isTileSolid(int tileX, int tileY) {\n    if (tileX < 0 || tileX >= TILEMAP_WIDTH ||\n        tileY < 0 || tileY >= TILEMAP_HEIGHT) {\n        return true; // Out of bounds = solid\n    }\n\n    uint8_t tileIndex = TILEMAP_INDICES[tileY * TILEMAP_WIDTH + tileX];\n    return tileIndex != 0; // 0 = empty, others = solid\n}\n\nbool checkCollision(float x, float y, int width, int height) {\n    // Convert world position to tile coordinates\n    int tileX1 = static_cast<int>(x) / TILE_SIZE;\n    int tileY1 = static_cast<int>(y) / TILE_SIZE;\n    int tileX2 = static_cast<int>(x + width) / TILE_SIZE;\n    int tileY2 = static_cast<int>(y + height) / TILE_SIZE;\n\n    // Check all tiles actor overlaps\n    for (int ty = tileY1; ty <= tileY2; ty++) {\n        for (int tx = tileX1; tx <= tileX2; tx++) {\n            if (isTileSolid(tx, ty)) {\n                return true; // Collision!\n            }\n        }\n    }\n\n    return false; // No collision\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/advanced_graphics/tilemaps/#tiles-not-appearing","title":"Tiles Not Appearing","text":"
    • Verify tile indices are correct (0 = first tile, 1 = second, etc.)
    • Check tilemap dimensions match indices array size
    • Ensure tiles array has enough entries
    • Verify tile size matches sprite size
    "},{"location":"manual/advanced_graphics/tilemaps/#performance-issues","title":"Performance Issues","text":"
    • Reduce tilemap size
    • Use smaller tiles
    • Limit number of unique tiles
    • Test viewport culling
    "},{"location":"manual/advanced_graphics/tilemaps/#scrolling-problems","title":"Scrolling Problems","text":"
    • Ensure camera is applied before drawing tilemap
    • Check tilemap position matches camera offset
    • Verify tilemap boundaries are correct
    • Test with simple tilemap first
    "},{"location":"manual/advanced_graphics/tilemaps/#next-steps","title":"Next Steps","text":"

    Now that you understand tilemaps, learn about: - Particles and Effects - Add visual effects - Cameras and Scrolling - Combine with scrolling - Performance Optimization - Optimize rendering

    See also: - API Reference - TileMap - API Reference - Renderer - Manual - Cameras and Scrolling

    "},{"location":"manual/game_development/audio/","title":"Audio","text":"

    PixelRoot32 includes a complete NES-like audio system with 4 channels for sound effects and background music. This guide shows you how to add sound and music to your games.

    "},{"location":"manual/game_development/audio/#audio-configuration","title":"Audio Configuration","text":"

    Before using audio, you need to configure an AudioBackend. This is done when creating the Engine:

    "},{"location":"manual/game_development/audio/#esp32-internal-dac","title":"ESP32: Internal DAC","text":"
    #include <drivers/esp32/ESP32_DAC_AudioBackend.h>\n\nconst int DAC_PIN = 25; // GPIO 25 or 26\npr32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(DAC_PIN, 11025);\n\npr32::audio::AudioConfig audioConfig(&audioBackend, audioBackend.getSampleRate());\n
    "},{"location":"manual/game_development/audio/#esp32-external-i2s-dac","title":"ESP32: External I2S DAC","text":"
    #include <drivers/esp32/ESP32_I2S_AudioBackend.h>\n\nconst int I2S_BCLK = 26;  // Bit clock\nconst int I2S_LRCK = 25;  // Left/Right clock\nconst int I2S_DOUT = 22;  // Data out\n\npr32::drivers::esp32::ESP32_I2S_AudioBackend audioBackend(\n    I2S_BCLK, I2S_LRCK, I2S_DOUT, 22050\n);\n\npr32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n
    "},{"location":"manual/game_development/audio/#native-pc-sdl2","title":"Native (PC): SDL2","text":"
    #include <drivers/native/SDL2_AudioBackend.h>\n\npr32::drivers::native::SDL2_AudioBackend audioBackend(22050, 1024);\n\npr32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n
    "},{"location":"manual/game_development/audio/#sound-effects","title":"Sound Effects","text":"

    Sound effects are created using AudioEvent structures and played through the AudioEngine.

    "},{"location":"manual/game_development/audio/#audioevent-structure","title":"AudioEvent Structure","text":"
    #include <audio/AudioTypes.h>\n\npr32::audio::AudioEvent soundEffect{};\nsoundEffect.type = pr32::audio::WaveType::PULSE;  // Waveform type\nsoundEffect.frequency = 1500.0f;                  // Frequency in Hz\nsoundEffect.duration = 0.12f;                      // Duration in seconds\nsoundEffect.volume = 0.8f;                         // Volume (0.0 to 1.0)\nsoundEffect.duty = 0.5f;                           // Duty cycle (for PULSE only)\n
    "},{"location":"manual/game_development/audio/#wave-types","title":"Wave Types","text":"

    PixelRoot32 supports three wave types:

    • PULSE: Square wave with variable duty cycle
    • Duty cycles: 0.125 (thin), 0.25 (classic NES), 0.5 (symmetric), 0.75 (fat)
    • Good for: Beeps, jumps, UI sounds, leads

    • TRIANGLE: Triangle wave (fixed volume/duty)

    • Softer, smoother sound
    • Good for: Bass lines, pads, background tones

    • NOISE: Pseudo-random noise

    • Harsh, chaotic sound
    • Good for: Explosions, hits, impacts, drums
    "},{"location":"manual/game_development/audio/#playing-sound-effects","title":"Playing Sound Effects","text":"
    // Get the audio engine\nauto& audio = engine.getAudioEngine();\n\n// Create and play a sound\npr32::audio::AudioEvent jumpSound{};\njumpSound.type = pr32::audio::WaveType::PULSE;\njumpSound.frequency = 800.0f;\njumpSound.duration = 0.1f;\njumpSound.volume = 0.7f;\njumpSound.duty = 0.25f;\n\naudio.playEvent(jumpSound);\n
    "},{"location":"manual/game_development/audio/#common-sound-effects","title":"Common Sound Effects","text":"

    Here are some example sound effects you can use:

    namespace SoundEffects {\n    // Jump sound\n    inline pr32::audio::AudioEvent jump() {\n        pr32::audio::AudioEvent evt{};\n        evt.type = pr32::audio::WaveType::PULSE;\n        evt.frequency = 600.0f;\n        evt.duration = 0.1f;\n        evt.volume = 0.7f;\n        evt.duty = 0.25f;\n        return evt;\n    }\n\n    // Coin/collect sound\n    inline pr32::audio::AudioEvent coin() {\n        pr32::audio::AudioEvent evt{};\n        evt.type = pr32::audio::WaveType::PULSE;\n        evt.frequency = 1500.0f;\n        evt.duration = 0.12f;\n        evt.volume = 0.8f;\n        evt.duty = 0.5f;\n        return evt;\n    }\n\n    // Explosion\n    inline pr32::audio::AudioEvent explosion() {\n        pr32::audio::AudioEvent evt{};\n        evt.type = pr32::audio::WaveType::NOISE;\n        evt.frequency = 200.0f;\n        evt.duration = 0.3f;\n        evt.volume = 0.9f;\n        return evt;\n    }\n\n    // Hit/damage\n    inline pr32::audio::AudioEvent hit() {\n        pr32::audio::AudioEvent evt{};\n        evt.type = pr32::audio::WaveType::NOISE;\n        evt.frequency = 300.0f;\n        evt.duration = 0.15f;\n        evt.volume = 0.6f;\n        return evt;\n    }\n}\n\n// Usage\naudio.playEvent(SoundEffects::jump());\n
    "},{"location":"manual/game_development/audio/#background-music","title":"Background Music","text":"

    Background music uses the MusicPlayer system, which sequences notes over time.

    "},{"location":"manual/game_development/audio/#music-notes","title":"Music Notes","text":"

    Music is built from MusicNote structures:

    #include <audio/AudioMusicTypes.h>\n\nusing namespace pr32::audio;\n\nMusicNote note{};\nnote.note = Note::C;        // Musical note (C, D, E, F, G, A, B, or Rest)\nnote.octave = 4;            // Octave (0-8)\nnote.duration = 0.2f;       // Duration in seconds\nnote.volume = 0.7f;         // Volume (0.0 to 1.0)\n
    "},{"location":"manual/game_development/audio/#instrument-presets","title":"Instrument Presets","text":"

    For convenience, use predefined instrument presets:

    using namespace pr32::audio;\n\n// Available presets:\n// - INSTR_PULSE_LEAD: Main lead pulse (octave 4)\n// - INSTR_PULSE_BASS: Bass pulse (octave 3)\n// - INSTR_PULSE_CHIP_HIGH: High-pitched chiptune (octave 5)\n// - INSTR_TRIANGLE_PAD: Soft triangle pad (octave 4)\n
    "},{"location":"manual/game_development/audio/#creating-a-melody","title":"Creating a Melody","text":"
    #include <audio/AudioMusicTypes.h>\n\nusing namespace pr32::audio;\n\n// Define melody notes\nstatic const MusicNote MELODY_NOTES[] = {\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::E, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::G, 0.25f),\n    makeRest(0.10f),  // Rest (silence)\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::E, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::G, 0.25f),\n    makeRest(0.10f),\n};\n\n// Create music track\nstatic const MusicTrack GAME_MUSIC = {\n    MELODY_NOTES,                              // notes array\n    sizeof(MELODY_NOTES) / sizeof(MusicNote),  // note count\n    true,                                       // loop\n    WaveType::PULSE,                           // channel type\n    0.5f                                       // duty cycle\n};\n
    "},{"location":"manual/game_development/audio/#playing-music","title":"Playing Music","text":"
    // Get the music player\nauto& music = engine.getMusicPlayer();\n\n// Play a track\nmusic.play(GAME_MUSIC);\n\n// Control playback\nmusic.stop();   // Stop playback\nmusic.pause();  // Pause (time doesn't advance)\nmusic.resume(); // Resume after pause\n\n// Check status\nif (music.isPlaying()) {\n    // Music is currently playing\n}\n
    "},{"location":"manual/game_development/audio/#music-in-scene","title":"Music in Scene","text":"

    Typically, you start music in your scene's init():

    void MyGameScene::init() override {\n    // Start background music\n    engine.getMusicPlayer().play(GAME_MUSIC);\n\n    // ... rest of initialization\n}\n
    "},{"location":"manual/game_development/audio/#master-volume","title":"Master Volume","text":"

    Control overall volume without changing individual sounds:

    auto& audio = engine.getAudioEngine();\n\n// Set master volume (0.0 to 1.0)\naudio.setMasterVolume(0.5f); // 50% volume\n\n// Get current volume\nfloat currentVolume = audio.getMasterVolume();\n
    "},{"location":"manual/game_development/audio/#complete-example","title":"Complete Example","text":"

    Here's a complete example combining sound effects and music:

    #include <core/Scene.h>\n#include <audio/AudioTypes.h>\n#include <audio/AudioMusicTypes.h>\n\nusing namespace pr32::audio;\n\n// Background music\nstatic const MusicNote GAME_MELODY[] = {\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::E, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::G, 0.25f),\n    makeRest(0.10f),\n};\n\nstatic const MusicTrack BACKGROUND_MUSIC = {\n    GAME_MELODY,\n    sizeof(GAME_MELODY) / sizeof(MusicNote),\n    true,  // loop\n    WaveType::PULSE,\n    0.5f\n};\n\nclass AudioExampleScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Start background music\n        engine.getMusicPlayer().play(BACKGROUND_MUSIC);\n\n        // Set master volume\n        engine.getAudioEngine().setMasterVolume(0.8f);\n    }\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        auto& audio = engine.getAudioEngine();\n\n        // Play sound effect on button press\n        if (input.isButtonPressed(4)) { // Button A\n            AudioEvent jumpSound{};\n            jumpSound.type = WaveType::PULSE;\n            jumpSound.frequency = 800.0f;\n            jumpSound.duration = 0.1f;\n            jumpSound.volume = 0.7f;\n            jumpSound.duty = 0.25f;\n\n            audio.playEvent(jumpSound);\n        }\n\n        Scene::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/game_development/audio/#designing-nes-like-sounds","title":"Designing NES-like Sounds","text":""},{"location":"manual/game_development/audio/#frequency-guidelines","title":"Frequency Guidelines","text":"
    • Low frequencies (200-400 Hz): Bass, impacts, explosions
    • Mid frequencies (400-1000 Hz): Main sounds, jumps, UI
    • High frequencies (1000-2000 Hz): Beeps, coins, pickups
    • Very high (2000+ Hz): Sharp sounds, alerts
    "},{"location":"manual/game_development/audio/#duration-guidelines","title":"Duration Guidelines","text":"
    • Short (0.05-0.1s): UI clicks, small effects
    • Medium (0.1-0.2s): Jumps, hits, pickups
    • Long (0.2-0.5s): Explosions, power-ups, transitions
    "},{"location":"manual/game_development/audio/#duty-cycle-pulse-only","title":"Duty Cycle (PULSE only)","text":"
    • 0.125: Thin, sharp, piercing
    • 0.25: Classic NES lead sound
    • 0.5: Symmetric, full, fat
    • 0.75: Very fat, bass-like
    "},{"location":"manual/game_development/audio/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/audio/#sound-design","title":"Sound Design","text":"
    • Keep sounds short: Long sounds can overlap and cause issues
    • Use appropriate volumes: 0.6-0.8 is usually good for effects
    • Vary frequencies: Don't use the same frequency for everything
    • Test on hardware: ESP32 audio may sound different than PC
    "},{"location":"manual/game_development/audio/#music","title":"Music","text":"
    • Use one channel for music: Leave other channels for SFX
    • Keep melodies simple: Complex melodies can be hard to follow
    • Loop seamlessly: End your melody where it can loop naturally
    • Consider tempo: Faster games need faster music
    "},{"location":"manual/game_development/audio/#performance","title":"Performance","text":"
    • Limit simultaneous sounds: Only 4 channels total
    • Music uses one channel: Plan your SFX accordingly
    • Don't spam sounds: Too many sounds can cause audio glitches
    • Use master volume: Easier than adjusting individual sounds
    "},{"location":"manual/game_development/audio/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/audio/#sound-effect-helper-function","title":"Sound Effect Helper Function","text":"
    void playJumpSound() {\n    auto& audio = engine.getAudioEngine();\n    AudioEvent evt{};\n    evt.type = WaveType::PULSE;\n    evt.frequency = 600.0f;\n    evt.duration = 0.1f;\n    evt.volume = 0.7f;\n    evt.duty = 0.25f;\n    audio.playEvent(evt);\n}\n
    "},{"location":"manual/game_development/audio/#music-state-management","title":"Music State Management","text":"
    class GameScene : public Scene {\n    bool musicStarted = false;\n\n    void init() override {\n        // Don't start music here if scene can be re-initialized\n    }\n\n    void update(unsigned long deltaTime) override {\n        if (!musicStarted) {\n            engine.getMusicPlayer().play(GAME_MUSIC);\n            musicStarted = true;\n        }\n        Scene::update(deltaTime);\n    }\n};\n
    "},{"location":"manual/game_development/audio/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/game_development/audio/#no-sound","title":"No Sound","text":"
    • Check audio backend is configured correctly
    • Verify sample rate matches backend
    • Check master volume is not 0
    • Ensure audio is initialized (engine.init())
    "},{"location":"manual/game_development/audio/#distorted-sound","title":"Distorted Sound","text":"
    • Lower volume levels
    • Reduce sample rate (ESP32 DAC works better at 11025 Hz)
    • Check for too many simultaneous sounds
    • Verify hardware connections (ESP32)
    "},{"location":"manual/game_development/audio/#music-not-playing","title":"Music Not Playing","text":"
    • Check music.isPlaying() status
    • Ensure track is properly defined
    • Verify MusicPlayer is updated (happens automatically)
    • Check that music channel is not being used by SFX
    "},{"location":"manual/game_development/audio/#next-steps","title":"Next Steps","text":"

    Now that you can add audio, learn about: - NES Audio Reference - Advanced audio techniques - Physics and Collisions - Make objects interact - User Interface - Create menus and HUDs

    See also: - API Reference - AudioEngine - API Reference - MusicPlayer - API Reference - Audio Types - Manual - Audio Overview

    "},{"location":"manual/game_development/basic_rendering/","title":"Basic Rendering","text":"

    Rendering is how you draw everything on screen. This guide covers the fundamental drawing operations in PixelRoot32: primitives, sprites, and text.

    "},{"location":"manual/game_development/basic_rendering/#accessing-the-renderer","title":"Accessing the Renderer","text":"

    You can access the renderer in two ways:

    "},{"location":"manual/game_development/basic_rendering/#from-the-engine","title":"From the Engine","text":"
    auto& renderer = engine.getRenderer();\n
    "},{"location":"manual/game_development/basic_rendering/#from-a-scene","title":"From a Scene","text":"
    void MyScene::draw(pixelroot32::graphics::Renderer& renderer) override {\n    // renderer is passed as parameter\n    renderer.drawFilledRectangle(0, 0, 240, 240, Color::Black);\n}\n
    "},{"location":"manual/game_development/basic_rendering/#drawing-primitives","title":"Drawing Primitives","text":""},{"location":"manual/game_development/basic_rendering/#pixels","title":"Pixels","text":"

    Draw a single pixel:

    renderer.drawPixel(100, 100, pixelroot32::graphics::Color::White);\n
    "},{"location":"manual/game_development/basic_rendering/#lines","title":"Lines","text":"

    Draw a line between two points:

    renderer.drawLine(10, 10, 200, 200, pixelroot32::graphics::Color::Red);\n
    "},{"location":"manual/game_development/basic_rendering/#rectangles","title":"Rectangles","text":"

    Draw a rectangle outline:

    renderer.drawRectangle(50, 50, 100, 80, pixelroot32::graphics::Color::Blue);\n

    Draw a filled rectangle:

    renderer.drawFilledRectangle(50, 50, 100, 80, pixelroot32::graphics::Color::Blue);\n
    "},{"location":"manual/game_development/basic_rendering/#circles","title":"Circles","text":"

    Draw a circle outline:

    renderer.drawCircle(120, 120, 30, pixelroot32::graphics::Color::Green);\n

    Draw a filled circle:

    renderer.drawFilledCircle(120, 120, 30, pixelroot32::graphics::Color::Green);\n
    "},{"location":"manual/game_development/basic_rendering/#simple-sprites-1bpp","title":"Simple Sprites (1bpp)","text":"

    Sprites are the primary way to draw game graphics. The standard format is 1bpp (1 bit per pixel), which is memory-efficient and perfect for retro-style graphics.

    "},{"location":"manual/game_development/basic_rendering/#creating-a-sprite-manually","title":"Creating a Sprite Manually","text":"

    A 1bpp sprite is defined as an array of uint16_t, where each value represents one row:

    #include <graphics/Renderer.h>\n\n// Example: 8x8 sprite (8 rows, each row is a uint16_t)\n// Bit 0 = leftmost pixel, bit 7 = rightmost pixel\nstatic const uint16_t MY_SPRITE_DATA[] = {\n    0b00111100,  // Row 0:   ..####..\n    0b01111110,  // Row 1:  .######.\n    0b11111111,  // Row 2:  ########\n    0b11111111,  // Row 3:  ########\n    0b11111111,  // Row 4:  ########\n    0b01111110,  // Row 5:  .######.\n    0b00111100,  // Row 6:   ..####..\n    0b00000000   // Row 7:  ........\n};\n\n// Create sprite descriptor\nstatic const pixelroot32::graphics::Sprite MY_SPRITE = {\n    MY_SPRITE_DATA,  // data pointer\n    8,                // width\n    8                 // height\n};\n
    "},{"location":"manual/game_development/basic_rendering/#drawing-a-sprite","title":"Drawing a Sprite","text":"
    renderer.drawSprite(\n    MY_SPRITE,                                    // sprite\n    100,                                          // x position\n    100,                                          // y position\n    pixelroot32::graphics::Color::White,         // color\n    false                                         // flipX (optional, default false)\n);\n
    "},{"location":"manual/game_development/basic_rendering/#sprite-bit-convention","title":"Sprite Bit Convention","text":"
    • Each uint16_t represents one row
    • Bit 0 (rightmost bit) = leftmost pixel
    • Bit (width - 1) = rightmost pixel
    • 0 = transparent/off, 1 = on (colored)

    Example: For an 8-pixel wide sprite:

    Row value: 0b00111100\nPixels:    ..####..\n           ^      ^\n         bit 7  bit 0 (leftmost)\n

    "},{"location":"manual/game_development/basic_rendering/#flipping-sprites","title":"Flipping Sprites","text":"

    You can flip a sprite horizontally:

    renderer.drawSprite(MY_SPRITE, 100, 100, Color::White, true); // flipped\n
    "},{"location":"manual/game_development/basic_rendering/#text-rendering","title":"Text Rendering","text":"

    PixelRoot32 uses a native bitmap font system for pixel-perfect text rendering.

    "},{"location":"manual/game_development/basic_rendering/#drawing-text","title":"Drawing Text","text":"
    renderer.drawText(\n    \"Hello World!\",                           // text string\n    10,                                       // x position\n    20,                                       // y position\n    pixelroot32::graphics::Color::White,     // color\n    1                                         // size multiplier (1=normal, 2=double, etc.)\n);\n
    "},{"location":"manual/game_development/basic_rendering/#centered-text","title":"Centered Text","text":"
    renderer.drawTextCentered(\n    \"Game Over\",                              // text\n    120,                                      // y position (centered horizontally)\n    pixelroot32::graphics::Color::Red,       // color\n    2                                         // size\n);\n
    "},{"location":"manual/game_development/basic_rendering/#text-size","title":"Text Size","text":"

    The size parameter multiplies the font size: - 1 = normal size (5x7 pixels per character with default font) - 2 = double size (10x14 pixels) - 3 = triple size (15x21 pixels) - etc.

    "},{"location":"manual/game_development/basic_rendering/#supported-characters","title":"Supported Characters","text":"

    The default font (FONT_5X7) supports ASCII characters 32-126: - Letters: A-Z, a-z - Numbers: 0-9 - Symbols: !@#$%^&*()_+-=[]{}|;:'\",.<>?/ etc.

    "},{"location":"manual/game_development/basic_rendering/#render-layers","title":"Render Layers","text":"

    Entities are drawn in order based on their renderLayer property:

    • Layer 0 (Background): Drawn first (behind everything)
    • Layer 1 (Gameplay): Drawn second (main game objects)
    • Layer 2 (UI): Drawn last (on top of everything)

    Set the render layer when creating an entity:

    myEntity->setRenderLayer(0); // Background\nmyEntity->setRenderLayer(1); // Gameplay (default)\nmyEntity->setRenderLayer(2); // UI\n
    "},{"location":"manual/game_development/basic_rendering/#complete-example","title":"Complete Example","text":"

    Here's a complete example that draws various primitives and a sprite:

    #include <core/Scene.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\n// Simple sprite data (8x8 circle)\nstatic const uint16_t CIRCLE_SPRITE_DATA[] = {\n    0b00111100,\n    0b01111110,\n    0b11111111,\n    0b11111111,\n    0b11111111,\n    0b11111111,\n    0b01111110,\n    0b00111100\n};\n\nstatic const pixelroot32::graphics::Sprite CIRCLE_SPRITE = {\n    CIRCLE_SPRITE_DATA,\n    8,\n    8\n};\n\nclass RenderingExampleScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Set palette\n        pixelroot32::graphics::setPalette(\n            pixelroot32::graphics::PaletteType::NES\n        );\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw background (layer 0)\n        renderer.drawFilledRectangle(0, 0, 240, 240, \n            pixelroot32::graphics::Color::Navy);\n\n        // Draw primitives (layer 1)\n        renderer.drawRectangle(10, 10, 100, 80, \n            pixelroot32::graphics::Color::White);\n        renderer.drawFilledCircle(120, 120, 30, \n            pixelroot32::graphics::Color::Red);\n        renderer.drawLine(0, 0, 240, 240, \n            pixelroot32::graphics::Color::Yellow);\n\n        // Draw sprite\n        renderer.drawSprite(CIRCLE_SPRITE, 50, 50, \n            pixelroot32::graphics::Color::Cyan);\n\n        // Draw text (layer 2 - UI)\n        renderer.drawText(\"Score: 100\", 10, 10, \n            pixelroot32::graphics::Color::White, 1);\n        renderer.drawTextCentered(\"Rendering Demo\", 200, \n            pixelroot32::graphics::Color::Yellow, 2);\n\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/game_development/basic_rendering/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/basic_rendering/#performance","title":"Performance","text":"
    • Minimize draw calls: Batch similar operations when possible
    • Use render layers efficiently: Don't mix layers unnecessarily
    • Avoid drawing off-screen: Check bounds before drawing
    • Reuse sprites: Define sprites once, use many times
    "},{"location":"manual/game_development/basic_rendering/#organization","title":"Organization","text":"
    • Define sprites as static const: Keep them in flash memory
    • Use meaningful names: Name your sprites clearly
    • Group related sprites: Organize sprite data logically
    • Document complex sprites: Comment sprite bit patterns if needed
    "},{"location":"manual/game_development/basic_rendering/#text","title":"Text","text":"
    • Avoid frequent text updates: Text rendering has overhead
    • Use appropriate sizes: Larger text uses more memory
    • Cache text dimensions: Use FontManager::textWidth() if needed
    • Keep text simple: Complex formatting is not supported
    "},{"location":"manual/game_development/basic_rendering/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/basic_rendering/#drawing-a-background","title":"Drawing a Background","text":"
    void drawBackground(Renderer& renderer) {\n    // Solid color background\n    renderer.drawFilledRectangle(0, 0, 240, 240, Color::Black);\n\n    // Or use a tilemap (see Advanced Graphics section)\n}\n
    "},{"location":"manual/game_development/basic_rendering/#drawing-a-hud","title":"Drawing a HUD","text":"
    void drawHUD(Renderer& renderer, int score, int lives) {\n    char buffer[32];\n    snprintf(buffer, sizeof(buffer), \"Score: %d\", score);\n    renderer.drawText(buffer, 10, 10, Color::White, 1);\n\n    snprintf(buffer, sizeof(buffer), \"Lives: %d\", lives);\n    renderer.drawText(buffer, 10, 20, Color::White, 1);\n}\n
    "},{"location":"manual/game_development/basic_rendering/#drawing-multiple-sprites","title":"Drawing Multiple Sprites","text":"
    void drawSpriteArray(Renderer& renderer, const Sprite& sprite, \n                     int count, int startX, int startY, int spacing) {\n    for (int i = 0; i < count; i++) {\n        int x = startX + (i * (sprite.width + spacing));\n        renderer.drawSprite(sprite, x, startY, Color::White);\n    }\n}\n
    "},{"location":"manual/game_development/basic_rendering/#next-steps","title":"Next Steps","text":"

    Now that you can draw basic graphics, learn about: - Sprites and Animation - Advanced sprite techniques - Input and Control - Make your game interactive - Palettes - Use different color schemes

    See also: - API Reference - Renderer - API Reference - Sprite - Render Layers

    "},{"location":"manual/game_development/input_and_control/","title":"Input and Control","text":"

    Handling user input is essential for any interactive game. This guide covers how to read and process input from buttons (ESP32) or keyboard (Native) in PixelRoot32.

    "},{"location":"manual/game_development/input_and_control/#input-configuration","title":"Input Configuration","text":"

    Before you can read input, you need to configure the InputManager. This is done when creating the Engine:

    "},{"location":"manual/game_development/input_and_control/#esp32-configuration","title":"ESP32 Configuration","text":"
    #include <input/InputConfig.h>\n\n// InputConfig(buttonCount, UP, DOWN, LEFT, RIGHT, A, B)\npr32::input::InputConfig inputConfig(\n    6,      // Total number of buttons\n    32,     // UP button GPIO pin\n    27,     // DOWN button GPIO pin\n    33,     // LEFT button GPIO pin\n    14,     // RIGHT button GPIO pin\n    13,     // A button GPIO pin\n    12      // B button GPIO pin\n);\n
    "},{"location":"manual/game_development/input_and_control/#native-pc-configuration","title":"Native (PC) Configuration","text":"
    #include <SDL2/SDL.h>\n#include <input/InputConfig.h>\n\n// InputConfig(buttonCount, UP, DOWN, LEFT, RIGHT, A, B)\npr32::input::InputConfig inputConfig(\n    6,                      // Total number of buttons\n    SDL_SCANCODE_UP,        // UP key\n    SDL_SCANCODE_DOWN,      // DOWN key\n    SDL_SCANCODE_LEFT,      // LEFT key\n    SDL_SCANCODE_RIGHT,     // RIGHT key\n    SDL_SCANCODE_SPACE,     // A button (Space)\n    SDL_SCANCODE_RETURN     // B button (Enter)\n);\n
    "},{"location":"manual/game_development/input_and_control/#reading-input","title":"Reading Input","text":"

    Access the InputManager through the Engine:

    auto& input = engine.getInputManager();\n
    "},{"location":"manual/game_development/input_and_control/#input-states","title":"Input States","text":"

    The InputManager provides four different ways to check button state:

    "},{"location":"manual/game_development/input_and_control/#1-isbuttonpressed","title":"1. isButtonPressed()","text":"

    Returns true only on the frame when the button was just pressed:

    if (input.isButtonPressed(4)) { // Button A (index 4)\n    // This code runs only once when button is first pressed\n    jump();\n}\n

    Use for: Actions that should trigger once per press (jump, shoot, select menu item).

    "},{"location":"manual/game_development/input_and_control/#2-isbuttonreleased","title":"2. isButtonReleased()","text":"

    Returns true only on the frame when the button was just released:

    if (input.isButtonReleased(4)) {\n    // This code runs only once when button is released\n    stopCharging();\n}\n

    Use for: Actions that trigger on release (charge attacks, menu confirmation).

    "},{"location":"manual/game_development/input_and_control/#3-isbuttondown","title":"3. isButtonDown()","text":"

    Returns true while the button is currently held down:

    if (input.isButtonDown(2)) { // LEFT button (index 2)\n    // This code runs every frame while button is held\n    playerX -= speed * (deltaTime * 0.001f);\n}\n

    Use for: Continuous actions (movement, holding, charging).

    "},{"location":"manual/game_development/input_and_control/#4-isbuttonclicked","title":"4. isButtonClicked()","text":"

    Returns true when the button was pressed and then released:

    if (input.isButtonClicked(4)) {\n    // This code runs once per click (press + release cycle)\n    toggleMenu();\n}\n

    Use for: Toggle actions, menu selections, click interactions.

    "},{"location":"manual/game_development/input_and_control/#button-indices","title":"Button Indices","text":"

    The button indices correspond to the order in InputConfig:

    • Index 0: UP
    • Index 1: DOWN
    • Index 2: LEFT
    • Index 3: RIGHT
    • Index 4: A button
    • Index 5: B button

    For convenience, you can define constants:

    namespace Buttons {\n    constexpr uint8_t UP = 0;\n    constexpr uint8_t DOWN = 1;\n    constexpr uint8_t LEFT = 2;\n    constexpr uint8_t RIGHT = 3;\n    constexpr uint8_t A = 4;\n    constexpr uint8_t B = 5;\n}\n\n// Usage\nif (input.isButtonPressed(Buttons::A)) {\n    // ...\n}\n
    "},{"location":"manual/game_development/input_and_control/#character-control-example","title":"Character Control Example","text":"

    Here's a complete example of character movement:

    #include <core/Actor.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\nclass PlayerActor : public pixelroot32::core::Actor {\npublic:\n    float speed = 100.0f; // pixels per second\n\n    PlayerActor(float x, float y)\n        : Actor(x, y, 16, 16) {\n        setRenderLayer(1);\n    }\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        float dt = deltaTime * 0.001f; // Convert to seconds\n\n        // Horizontal movement\n        if (input.isButtonDown(Buttons::LEFT)) {\n            x -= speed * dt;\n        }\n        if (input.isButtonDown(Buttons::RIGHT)) {\n            x += speed * dt;\n        }\n\n        // Vertical movement\n        if (input.isButtonDown(Buttons::UP)) {\n            y -= speed * dt;\n        }\n        if (input.isButtonDown(Buttons::DOWN)) {\n            y += speed * dt;\n        }\n\n        // Keep player on screen\n        if (x < 0) x = 0;\n        if (x > 224) x = 224;\n        if (y < 0) y = 0;\n        if (y > 224) y = 224;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x),\n            static_cast<int>(y),\n            width,\n            height,\n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Handle collisions\n    }\n};\n
    "},{"location":"manual/game_development/input_and_control/#jumping-example","title":"Jumping Example","text":"

    For platformer-style jumping:

    class PlatformerPlayer : public pixelroot32::core::PhysicsActor {\npublic:\n    bool canJump = true;\n    float jumpForce = 200.0f;\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        float dt = deltaTime * 0.001f;\n\n        // Horizontal movement\n        float moveSpeed = 80.0f;\n        if (input.isButtonDown(Buttons::LEFT)) {\n            setVelocity(-moveSpeed, vy);\n        } else if (input.isButtonDown(Buttons::RIGHT)) {\n            setVelocity(moveSpeed, vy);\n        } else {\n            setVelocity(0, vy);\n        }\n\n        // Jump (only on press, and only if on ground)\n        if (input.isButtonPressed(Buttons::A) && canJump) {\n            setVelocity(vx, -jumpForce);\n            canJump = false;\n        }\n\n        // Check if on ground (for jump reset)\n        auto collisionInfo = getWorldCollisionInfo();\n        if (collisionInfo.bottom) {\n            canJump = true;\n        }\n\n        PhysicsActor::update(deltaTime);\n    }\n};\n
    "},{"location":"manual/game_development/input_and_control/#shooting-example","title":"Shooting Example","text":"

    For shooting projectiles:

    class ShooterActor : public pixelroot32::core::Actor {\nprivate:\n    bool fireInputReady = true;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n\n        // Shooting (with cooldown)\n        if (input.isButtonPressed(Buttons::A) && fireInputReady) {\n            shoot();\n            fireInputReady = false;\n        }\n\n        // Reset fire input when button is released\n        if (input.isButtonReleased(Buttons::A)) {\n            fireInputReady = true;\n        }\n    }\n\n    void shoot() {\n        // Create projectile\n        // ...\n    }\n};\n
    "},{"location":"manual/game_development/input_and_control/#menu-navigation-example","title":"Menu Navigation Example","text":"

    For menu navigation:

    class MenuScene : public pixelroot32::core::Scene {\nprivate:\n    int selectedIndex = 0;\n    static const int MENU_ITEMS = 3;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n\n        // Navigate menu\n        if (input.isButtonPressed(Buttons::UP)) {\n            selectedIndex--;\n            if (selectedIndex < 0) selectedIndex = MENU_ITEMS - 1;\n        }\n\n        if (input.isButtonPressed(Buttons::DOWN)) {\n            selectedIndex++;\n            if (selectedIndex >= MENU_ITEMS) selectedIndex = 0;\n        }\n\n        // Select menu item\n        if (input.isButtonPressed(Buttons::A)) {\n            selectMenuItem(selectedIndex);\n        }\n\n        Scene::update(deltaTime);\n    }\n};\n
    "},{"location":"manual/game_development/input_and_control/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/input_and_control/#frame-rate-independence","title":"Frame-Rate Independence","text":"

    Always multiply movement by delta time:

    // \u2705 GOOD: Framerate-independent\nx += speed * (deltaTime * 0.001f);\n\n// \u274c BAD: Framerate-dependent\nx += speed;\n
    "},{"location":"manual/game_development/input_and_control/#input-debouncing","title":"Input Debouncing","text":"

    For actions that should only trigger once:

    // Use isButtonPressed() instead of isButtonDown()\nif (input.isButtonPressed(Buttons::A)) {\n    // Triggers once per press\n}\n
    "},{"location":"manual/game_development/input_and_control/#input-buffering","title":"Input Buffering","text":"

    For responsive controls, you can buffer input:

    class InputBuffer {\n    uint8_t bufferedInput = 0;\n\npublic:\n    void update(const InputManager& input) {\n        if (input.isButtonPressed(Buttons::A)) {\n            bufferedInput = Buttons::A;\n        }\n    }\n\n    bool hasBufferedInput() const { return bufferedInput != 0; }\n    uint8_t consumeInput() {\n        uint8_t result = bufferedInput;\n        bufferedInput = 0;\n        return result;\n    }\n};\n
    "},{"location":"manual/game_development/input_and_control/#multiple-input-methods","title":"Multiple Input Methods","text":"

    Support both button presses and held buttons:

    // Allow both tap and hold for rapid fire\nif (input.isButtonPressed(Buttons::A) || \n    (input.isButtonDown(Buttons::A) && rapidFireTimer <= 0)) {\n    shoot();\n    rapidFireTimer = RAPID_FIRE_DELAY;\n}\n
    "},{"location":"manual/game_development/input_and_control/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/input_and_control/#directional-input","title":"Directional Input","text":"
    float getHorizontalInput() {\n    auto& input = engine.getInputManager();\n    float dir = 0.0f;\n    if (input.isButtonDown(Buttons::LEFT)) dir -= 1.0f;\n    if (input.isButtonDown(Buttons::RIGHT)) dir += 1.0f;\n    return dir;\n}\n\nfloat getVerticalInput() {\n    auto& input = engine.getInputManager();\n    float dir = 0.0f;\n    if (input.isButtonDown(Buttons::UP)) dir -= 1.0f;\n    if (input.isButtonDown(Buttons::DOWN)) dir += 1.0f;\n    return dir;\n}\n
    "},{"location":"manual/game_development/input_and_control/#input-state-machine","title":"Input State Machine","text":"

    For complex input handling:

    enum class InputState {\n    IDLE,\n    PRESSED,\n    HELD,\n    RELEASED\n};\n\nInputState getButtonState(const InputManager& input, uint8_t button) {\n    if (input.isButtonPressed(button)) return InputState::PRESSED;\n    if (input.isButtonDown(button)) return InputState::HELD;\n    if (input.isButtonReleased(button)) return InputState::RELEASED;\n    return InputState::IDLE;\n}\n
    "},{"location":"manual/game_development/input_and_control/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/game_development/input_and_control/#button-not-responding","title":"Button Not Responding","text":"
    • Check button indices match your InputConfig
    • Verify GPIO pins (ESP32) or scancodes (Native) are correct
    • Ensure InputManager is being updated (happens automatically in Engine)
    "},{"location":"manual/game_development/input_and_control/#input-feels-laggy","title":"Input Feels Laggy","text":"
    • Ensure you're using deltaTime for movement
    • Check that input is read in update(), not draw()
    • Verify framerate is stable
    "},{"location":"manual/game_development/input_and_control/#multiple-triggers","title":"Multiple Triggers","text":"
    • Use isButtonPressed() instead of isButtonDown() for one-time actions
    • Implement input buffering or cooldown timers
    "},{"location":"manual/game_development/input_and_control/#next-steps","title":"Next Steps","text":"

    Now that you can handle input, learn about: - Audio - Add sound effects and music - Physics and Collisions - Make objects interact - User Interface - Create menus and HUDs

    See also: - API Reference - InputManager - API Reference - InputConfig - Manual - Input Overview

    "},{"location":"manual/game_development/physics_and_collisions/","title":"Physics and Collisions","text":"

    PixelRoot32 provides a physics system for moving objects and collision detection. This guide covers PhysicsActor for automatic physics and the collision system for detecting interactions between objects.

    "},{"location":"manual/game_development/physics_and_collisions/#physicsactor","title":"PhysicsActor","text":"

    A PhysicsActor is an Actor that automatically handles physics: velocity, gravity, friction, and world boundary collisions.

    "},{"location":"manual/game_development/physics_and_collisions/#creating-a-physicsactor","title":"Creating a PhysicsActor","text":"
    #include <core/PhysicsActor.h>\n\nclass Ball : public pixelroot32::core::PhysicsActor {\npublic:\n    Ball(float x, float y, float radius)\n        : PhysicsActor(x, y, radius * 2, radius * 2) {\n        setRenderLayer(1);\n\n        // Set physics properties\n        setRestitution(0.8f);  // Bounciness (0.8 = 80% bounce)\n        setFriction(0.1f);     // Friction (0.1 = slight friction)\n\n        // Set world boundaries\n        setWorldSize(240, 240);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Apply gravity\n        // (PhysicsActor handles this automatically, but you can add custom forces)\n\n        // Call parent update to apply physics\n        PhysicsActor::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        int radius = width / 2;\n        renderer.drawFilledCircle(\n            static_cast<int>(x + radius),\n            static_cast<int>(y + radius),\n            radius,\n            pixelroot32::graphics::Color::White\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Handle collision with other actors\n    }\n\n    void onWorldCollision() override {\n        // Called when hitting world boundaries\n        // You can play a sound effect here, for example\n    }\n};\n
    "},{"location":"manual/game_development/physics_and_collisions/#physics-properties","title":"Physics Properties","text":""},{"location":"manual/game_development/physics_and_collisions/#velocity","title":"Velocity","text":"

    Set the velocity directly:

    ball->setVelocity(100.0f, -50.0f); // Move right at 100 px/s, up at 50 px/s\n

    Or modify existing velocity:

    float vx = ball->vx; // Access velocity components (protected, use getter if needed)\nball->setVelocity(vx + 10.0f, ball->vy); // Accelerate horizontally\n
    "},{"location":"manual/game_development/physics_and_collisions/#restitution-bounciness","title":"Restitution (Bounciness)","text":"

    Controls how much energy is conserved in collisions:

    ball->setRestitution(1.0f);  // Perfect bounce (no energy loss)\nball->setRestitution(0.5f);  // 50% energy loss\nball->setRestitution(0.0f);  // No bounce (stops on impact)\n
    "},{"location":"manual/game_development/physics_and_collisions/#friction","title":"Friction","text":"

    Applies gradual velocity reduction:

    ball->setFriction(0.0f);  // No friction (slides forever)\nball->setFriction(0.5f);  // Moderate friction\nball->setFriction(1.0f);  // High friction (stops quickly)\n
    "},{"location":"manual/game_development/physics_and_collisions/#world-boundaries","title":"World Boundaries","text":"

    Set the playable area:

    // Set world size (used as default boundaries)\nball->setWorldSize(240, 240);\n\n// Or set custom boundaries\npixelroot32::core::LimitRect limits(10, 10, 230, 230); // Left, Top, Right, Bottom\nball->setLimits(limits);\n
    "},{"location":"manual/game_development/physics_and_collisions/#world-collision-detection","title":"World Collision Detection","text":"

    Check if the actor hit world boundaries:

    void update(unsigned long deltaTime) override {\n    PhysicsActor::update(deltaTime);\n\n    auto collisionInfo = getWorldCollisionInfo();\n\n    if (collisionInfo.left || collisionInfo.right) {\n        // Hit side walls\n    }\n\n    if (collisionInfo.top || collisionInfo.bottom) {\n        // Hit top/bottom walls\n    }\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#collision-system","title":"Collision System","text":"

    The collision system detects when Actors overlap and triggers callbacks.

    "},{"location":"manual/game_development/physics_and_collisions/#collision-layers","title":"Collision Layers","text":"

    Use bit flags to organize actors into groups:

    // Define layers (typically in GameLayers.h)\nnamespace Layers {\n    constexpr uint16_t PLAYER = 0x0001;    // Bit 0\n    constexpr uint16_t ENEMY = 0x0002;     // Bit 1\n    constexpr uint16_t PROJECTILE = 0x0004; // Bit 2\n    constexpr uint16_t WALL = 0x0008;      // Bit 3\n    constexpr uint16_t PICKUP = 0x0010;    // Bit 4\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#setting-up-collisions","title":"Setting Up Collisions","text":"

    Configure each actor's collision layer and mask:

    class PlayerActor : public pixelroot32::core::Actor {\npublic:\n    PlayerActor(float x, float y)\n        : Actor(x, y, 16, 16) {\n        // This actor belongs to the PLAYER layer\n        setCollisionLayer(Layers::PLAYER);\n\n        // This actor can collide with ENEMY, WALL, and PICKUP\n        setCollisionMask(Layers::ENEMY | Layers::WALL | Layers::PICKUP);\n    }\n\n    // ... rest of implementation\n};\n\nclass EnemyActor : public pixelroot32::core::Actor {\npublic:\n    EnemyActor(float x, float y)\n        : Actor(x, y, 16, 16) {\n        // This actor belongs to the ENEMY layer\n        setCollisionLayer(Layers::ENEMY);\n\n        // This actor can collide with PLAYER and PROJECTILE\n        setCollisionMask(Layers::PLAYER | Layers::PROJECTILE);\n    }\n\n    // ... rest of implementation\n};\n
    "},{"location":"manual/game_development/physics_and_collisions/#collision-detection","title":"Collision Detection","text":"

    Collisions are automatically detected by the Scene's CollisionSystem. You handle collisions in onCollision():

    void PlayerActor::onCollision(pixelroot32::core::Actor* other) override {\n    // Check what we collided with\n    if (other->isInLayer(Layers::ENEMY)) {\n        // Hit an enemy - take damage\n        takeDamage();\n    } else if (other->isInLayer(Layers::PICKUP)) {\n        // Hit a pickup - collect it\n        collectPickup(other);\n    } else if (other->isInLayer(Layers::WALL)) {\n        // Hit a wall - stop movement\n        stopMovement();\n    }\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#hitbox","title":"Hitbox","text":"

    Define the collision shape:

    pixelroot32::core::Rect getHitBox() override {\n    // Simple AABB (Axis-Aligned Bounding Box)\n    return {x, y, width, height};\n\n    // Or use a smaller hitbox for more forgiving collisions\n    // return {x + 2, y + 2, width - 4, height - 4};\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#complete-example-bouncing-ball","title":"Complete Example: Bouncing Ball","text":"
    #include <core/PhysicsActor.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\nclass BouncingBall : public pixelroot32::core::PhysicsActor {\npublic:\n    BouncingBall(float x, float y, float radius)\n        : PhysicsActor(x, y, radius * 2, radius * 2) {\n        setRenderLayer(1);\n\n        // Physics setup\n        setRestitution(0.9f);  // Very bouncy\n        setFriction(0.05f);    // Low friction\n        setWorldSize(240, 240); // World boundaries\n\n        // Initial velocity\n        setVelocity(50.0f, -30.0f);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Apply gravity\n        float gravity = 200.0f; // pixels per second squared\n        float dt = deltaTime * 0.001f;\n        setVelocity(vx, vy + gravity * dt);\n\n        // Update physics\n        PhysicsActor::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        int radius = width / 2;\n        renderer.drawFilledCircle(\n            static_cast<int>(x + radius),\n            static_cast<int>(y + radius),\n            radius,\n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Bounce off other objects\n        // (PhysicsActor handles world boundaries automatically)\n    }\n\n    void onWorldCollision() override {\n        // Play bounce sound when hitting walls\n        // (Implementation depends on your audio setup)\n    }\n};\n
    "},{"location":"manual/game_development/physics_and_collisions/#complete-example-platformer-player","title":"Complete Example: Platformer Player","text":"
    class PlatformerPlayer : public pixelroot32::core::PhysicsActor {\nprivate:\n    bool onGround = false;\n    float jumpForce = 250.0f;\n    float moveSpeed = 100.0f;\n\npublic:\n    PlatformerPlayer(float x, float y)\n        : PhysicsActor(x, y, 16, 16) {\n        setRenderLayer(1);\n        setFriction(0.3f);  // Ground friction\n        setWorldSize(240, 240);\n\n        // Collision setup\n        setCollisionLayer(Layers::PLAYER);\n        setCollisionMask(Layers::ENEMY | Layers::PLATFORM);\n    }\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        float dt = deltaTime * 0.001f;\n\n        // Horizontal movement\n        float moveDir = 0.0f;\n        if (input.isButtonDown(Buttons::LEFT)) moveDir -= 1.0f;\n        if (input.isButtonDown(Buttons::RIGHT)) moveDir += 1.0f;\n\n        setVelocity(moveDir * moveSpeed, vy);\n\n        // Apply gravity\n        float gravity = 300.0f;\n        setVelocity(vx, vy + gravity * dt);\n\n        // Jump\n        if (input.isButtonPressed(Buttons::A) && onGround) {\n            setVelocity(vx, -jumpForce);\n            onGround = false;\n        }\n\n        // Update physics\n        PhysicsActor::update(deltaTime);\n\n        // Check if on ground\n        auto collisionInfo = getWorldCollisionInfo();\n        onGround = collisionInfo.bottom;\n\n        // Also check collision with platforms\n        // (This would be handled in onCollision)\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        if (other->isInLayer(Layers::PLATFORM)) {\n            // Land on platform\n            auto platformRect = other->getHitBox();\n            if (y + height <= platformRect.y + 5) { // Within 5 pixels of top\n                y = platformRect.y - height;\n                vy = 0;\n                onGround = true;\n            }\n        } else if (other->isInLayer(Layers::ENEMY)) {\n            // Hit enemy\n            takeDamage();\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x),\n            static_cast<int>(y),\n            width,\n            height,\n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n};\n
    "},{"location":"manual/game_development/physics_and_collisions/#sweep-tests","title":"Sweep Tests","text":"

    For fast-moving projectiles, use sweep tests to detect collisions between positions:

    #include <physics/CollisionPrimitives.h>\n\nbool checkProjectileHit(PhysicsActor* projectile, Actor* target) {\n    // Get previous and current positions\n    float prevX = projectile->x - (projectile->vx * deltaTime * 0.001f);\n    float prevY = projectile->y - (projectile->vy * deltaTime * 0.001f);\n\n    // Create circles for sweep test\n    pixelroot32::physics::Circle startCircle;\n    startCircle.x = prevX + projectile->width / 2;\n    startCircle.y = prevY + projectile->height / 2;\n    startCircle.radius = projectile->width / 2;\n\n    pixelroot32::physics::Circle endCircle;\n    endCircle.x = projectile->x + projectile->width / 2;\n    endCircle.y = projectile->y + projectile->height / 2;\n    endCircle.radius = projectile->width / 2;\n\n    // Get target rectangle\n    auto targetRect = target->getHitBox();\n\n    // Perform sweep test\n    float tHit;\n    if (pixelroot32::physics::sweepCircleVsRect(\n        startCircle, endCircle, targetRect, tHit)) {\n        // Collision detected at time tHit (0.0 = at start, 1.0 = at end)\n        return true;\n    }\n\n    return false;\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/physics_and_collisions/#collision-layers_1","title":"Collision Layers","text":"
    • Plan your layers: Design the layer system before coding
    • Use bit flags: Makes combining layers easy with | operator
    • Keep it simple: Don't create too many layers
    • Document layers: Create a GameLayers.h file with all definitions
    "},{"location":"manual/game_development/physics_and_collisions/#physics","title":"Physics","text":"
    • Use appropriate values: Test gravity, speed, and forces
    • Frame-rate independence: Always use deltaTime
    • Limit world size: Keep boundaries reasonable
    • Test on hardware: Physics may behave differently on ESP32 vs PC
    "},{"location":"manual/game_development/physics_and_collisions/#performance","title":"Performance","text":"
    • Limit active actors: Fewer actors = faster collision checks
    • Use layers efficiently: Reduce unnecessary collision pairs
    • Simple hitboxes: AABB is fast, complex shapes are slow
    • Sweep tests sparingly: Only for fast-moving objects
    "},{"location":"manual/game_development/physics_and_collisions/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/physics_and_collisions/#collision-layer-helper","title":"Collision Layer Helper","text":"
    namespace CollisionLayers {\n    constexpr uint16_t PLAYER = 0x0001;\n    constexpr uint16_t ENEMY = 0x0002;\n    constexpr uint16_t PROJECTILE = 0x0004;\n    constexpr uint16_t WALL = 0x0008;\n    constexpr uint16_t PICKUP = 0x0010;\n\n    // Helper to check if actor is in specific layer\n    bool isPlayer(Actor* actor) {\n        return actor->isInLayer(PLAYER);\n    }\n\n    bool isEnemy(Actor* actor) {\n        return actor->isInLayer(ENEMY);\n    }\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#platform-collision","title":"Platform Collision","text":"
    void PlatformerPlayer::onCollision(Actor* other) override {\n    if (other->isInLayer(Layers::PLATFORM)) {\n        auto platform = other->getHitBox();\n\n        // Check if landing on top of platform\n        float playerBottom = y + height;\n        float platformTop = platform.y;\n\n        if (playerBottom <= platformTop + 5 && vy > 0) {\n            // Land on platform\n            y = platformTop - height;\n            vy = 0;\n            onGround = true;\n        }\n    }\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/game_development/physics_and_collisions/#collisions-not-detected","title":"Collisions Not Detected","text":"
    • Verify collision layers and masks overlap
    • Check that actors are added to the scene
    • Ensure Scene::update() is called
    • Verify hitboxes are correct
    "},{"location":"manual/game_development/physics_and_collisions/#physics-too-fastslow","title":"Physics Too Fast/Slow","text":"
    • Adjust values based on deltaTime
    • Check gravity and velocity values
    • Test on actual hardware (ESP32 may run slower)
    "},{"location":"manual/game_development/physics_and_collisions/#objects-passing-through","title":"Objects Passing Through","text":"
    • Use sweep tests for fast objects
    • Increase collision detection frequency
    • Check hitbox sizes match visual size
    "},{"location":"manual/game_development/physics_and_collisions/#next-steps","title":"Next Steps","text":"

    Now that you understand physics and collisions, learn about: - User Interface - Create menus and HUDs - Advanced Graphics - Advanced sprite techniques - Camera and Scrolling - Create scrolling levels

    See also: - API Reference - PhysicsActor - API Reference - CollisionSystem - Manual - Physics Overview - Manual - Collision Detection

    "},{"location":"manual/game_development/scenes_and_entities/","title":"Scenes and Entities","text":"

    Scenes and entities are the foundation of every PixelRoot32 game. This guide teaches you how to organize your game using scenes and create interactive game objects with entities.

    "},{"location":"manual/game_development/scenes_and_entities/#creating-a-scene","title":"Creating a Scene","text":"

    A Scene represents a screen or level in your game. To create a scene, inherit from pixelroot32::core::Scene and implement the three main methods:

    #include <core/Scene.h>\n#include <graphics/Renderer.h>\n\nclass MyGameScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Called once when the scene is initialized\n        // Set up your scene here: create entities, load resources, etc.\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Called every frame\n        // Update game logic here\n\n        // IMPORTANT: Always call parent update to update all entities\n        Scene::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Called every frame to draw\n        // Draw your scene here\n\n        // IMPORTANT: Always call parent draw to draw all entities\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/game_development/scenes_and_entities/#scene-lifecycle","title":"Scene Lifecycle","text":"
    1. init(): Called once when the scene is set as active
    2. Create and initialize entities
    3. Set up game state
    4. Load resources
    5. Configure palettes, audio, etc.

    6. update(deltaTime): Called every frame

    7. Process input
    8. Update game logic
    9. Handle collisions
    10. Must call Scene::update(deltaTime) to update all entities

    11. draw(renderer): Called every frame

    12. Draw background elements
    13. Draw UI elements
    14. Must call Scene::draw(renderer) to draw all entities
    "},{"location":"manual/game_development/scenes_and_entities/#basic-entities","title":"Basic Entities","text":"

    An Entity is any object in your game. To create an entity, inherit from pixelroot32::core::Entity:

    #include <core/Entity.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\nclass SimpleEntity : public pixelroot32::core::Entity {\npublic:\n    SimpleEntity(float x, float y)\n        : Entity(x, y, 16, 16, pixelroot32::core::EntityType::GENERIC) {\n        // Set render layer (0=background, 1=gameplay, 2=UI)\n        setRenderLayer(1);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Update entity logic\n        // For example, move the entity\n        this->x += 1.0f * (deltaTime * 0.001f); // Move right at 1 pixel per second\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw the entity\n        renderer.drawFilledRectangle(\n            static_cast<int>(x), \n            static_cast<int>(y), \n            width, \n            height, \n            pixelroot32::graphics::Color::Red\n        );\n    }\n};\n
    "},{"location":"manual/game_development/scenes_and_entities/#entity-properties","title":"Entity Properties","text":"

    Every entity has these properties:

    • x, y: Position in world space (float)
    • width, height: Dimensions (int)
    • isVisible: If false, draw() is not called
    • isEnabled: If false, update() is not called
    • renderLayer: Which layer to draw on (0, 1, or 2)
    "},{"location":"manual/game_development/scenes_and_entities/#adding-entities-to-a-scene","title":"Adding Entities to a Scene","text":"

    Add entities to your scene in init():

    void MyGameScene::init() override {\n    // Create entities\n    SimpleEntity* entity1 = new SimpleEntity(50, 50);\n    SimpleEntity* entity2 = new SimpleEntity(100, 100);\n\n    // Add them to the scene\n    addEntity(entity1);\n    addEntity(entity2);\n}\n

    The scene automatically manages these entities: - Calls update() on all enabled entities each frame - Calls draw() on all visible entities each frame - Handles cleanup when the scene is destroyed

    "},{"location":"manual/game_development/scenes_and_entities/#actors-entities-with-collisions","title":"Actors: Entities with Collisions","text":"

    An Actor is an entity that can participate in collision detection. Inherit from pixelroot32::core::Actor:

    #include <core/Actor.h>\n#include <physics/CollisionTypes.h>\n\nclass MyActor : public pixelroot32::core::Actor {\npublic:\n    MyActor(float x, float y, int w, int h)\n        : Actor(x, y, w, h) {\n        // Set collision layer (what group this actor belongs to)\n        setCollisionLayer(0x0001); // Example: layer 1\n\n        // Set collision mask (what groups this actor can collide with)\n        setCollisionMask(0x0002); // Example: can collide with layer 2\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Update actor logic\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw the actor\n    }\n\n    // REQUIRED: Define the hitbox for collision detection\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    // REQUIRED: Handle collisions\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // React to collision\n        // For example: take damage, destroy self, etc.\n    }\n};\n
    "},{"location":"manual/game_development/scenes_and_entities/#collision-layers-and-masks","title":"Collision Layers and Masks","text":"

    Collision layers use bit flags to organize actors into groups:

    // Define your layers (typically in a GameLayers.h file)\nnamespace Layers {\n    constexpr uint16_t PLAYER = 0x0001;  // Bit 0\n    constexpr uint16_t ENEMY = 0x0002;  // Bit 1\n    constexpr uint16_t PROJECTILE = 0x0004; // Bit 2\n    constexpr uint16_t WALL = 0x0008;   // Bit 3\n}\n\n// Set up a player actor\nplayer->setCollisionLayer(Layers::PLAYER);\nplayer->setCollisionMask(Layers::ENEMY | Layers::WALL); // Can collide with enemies and walls\n\n// Set up an enemy actor\nenemy->setCollisionLayer(Layers::ENEMY);\nenemy->setCollisionMask(Layers::PLAYER | Layers::PROJECTILE); // Can collide with player and projectiles\n

    The collision system only checks collisions between actors whose layers and masks overlap. This is much more efficient than checking every pair.

    "},{"location":"manual/game_development/scenes_and_entities/#scene-management","title":"Scene Management","text":""},{"location":"manual/game_development/scenes_and_entities/#setting-the-active-scene","title":"Setting the Active Scene","text":"

    From your main code, set the active scene:

    MyGameScene gameScene;\n\nvoid setup() {\n    engine.init();\n    gameScene.init();\n    engine.setScene(&gameScene); // Set as active scene\n}\n
    "},{"location":"manual/game_development/scenes_and_entities/#switching-scenes","title":"Switching Scenes","text":"

    To switch to a different scene:

    MenuScene menuScene;\nGameScene gameScene;\n\nvoid switchToGame() {\n    gameScene.init();\n    engine.setScene(&gameScene); // Replaces current scene\n}\n
    "},{"location":"manual/game_development/scenes_and_entities/#scene-stack-pushpop","title":"Scene Stack (Push/Pop)","text":"

    For menus and pause screens, use the scene stack:

    // Push a pause menu (game scene stays in background)\nvoid pauseGame() {\n    pauseMenu.init();\n    engine.getCurrentScene()->getSceneManager().pushScene(&pauseMenu);\n}\n\n// Pop the pause menu (resume game)\nvoid resumeGame() {\n    engine.getCurrentScene()->getSceneManager().popScene();\n}\n

    Note: Scene stack management is handled internally by the Engine's SceneManager. You typically access it through engine.getCurrentScene().

    "},{"location":"manual/game_development/scenes_and_entities/#complete-example","title":"Complete Example","text":"

    Here's a complete example of a scene with multiple entities:

    #include <core/Scene.h>\n#include <core/Entity.h>\n#include <core/Actor.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\n// A simple moving entity\nclass MovingBox : public pixelroot32::core::Entity {\npublic:\n    MovingBox(float x, float y) \n        : Entity(x, y, 20, 20, pixelroot32::core::EntityType::GENERIC),\n          speedX(50.0f), speedY(30.0f) {\n        setRenderLayer(1);\n    }\n\n    void update(unsigned long deltaTime) override {\n        float dt = deltaTime * 0.001f; // Convert to seconds\n\n        x += speedX * dt;\n        y += speedY * dt;\n\n        // Bounce off screen edges\n        if (x < 0 || x > 220) speedX = -speedX;\n        if (y < 0 || y > 220) speedY = -speedY;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x), \n            static_cast<int>(y), \n            width, \n            height, \n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n\nprivate:\n    float speedX, speedY;\n};\n\n// A simple actor that can collide\nclass CollidableBox : public pixelroot32::core::Actor {\npublic:\n    CollidableBox(float x, float y)\n        : Actor(x, y, 30, 30) {\n        setRenderLayer(1);\n        setCollisionLayer(0x0001);\n        setCollisionMask(0x0001);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Static actor, no movement\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x), \n            static_cast<int>(y), \n            width, \n            height, \n            pixelroot32::graphics::Color::Yellow\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Change color when collided\n        // (In a real game, you'd handle collision logic here)\n    }\n};\n\n// The scene\nclass ExampleScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Create and add entities\n        addEntity(new MovingBox(50, 50));\n        addEntity(new MovingBox(150, 100));\n        addEntity(new CollidableBox(100, 100));\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime); // Update all entities\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw background\n        renderer.drawFilledRectangle(0, 0, 240, 240, \n            pixelroot32::graphics::Color::Black);\n\n        Scene::draw(renderer); // Draw all entities\n    }\n};\n
    "},{"location":"manual/game_development/scenes_and_entities/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/scenes_and_entities/#entity-management","title":"Entity Management","text":"
    • Pre-allocate entities: Create entities in init(), not in update()
    • Reuse entities: Instead of creating/destroying, enable/disable entities
    • Limit entity count: MAX_ENTITIES = 32 per scene
    • Use object pooling: For frequently created/destroyed entities (projectiles, particles)
    "},{"location":"manual/game_development/scenes_and_entities/#scene-organization","title":"Scene Organization","text":"
    • One scene per screen: Menu, game, game over, etc.
    • Keep scenes focused: Each scene should have a single responsibility
    • Initialize in init(): Don't do heavy work in the constructor
    • Clean up properly: Remove entities when switching scenes
    "},{"location":"manual/game_development/scenes_and_entities/#collision-layers","title":"Collision Layers","text":"
    • Plan your layers: Design your layer system before coding
    • Use bit flags: Makes layer combinations easy
    • Keep it simple: Don't over-complicate with too many layers
    • Document your layers: Create a GameLayers.h file
    "},{"location":"manual/game_development/scenes_and_entities/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/scenes_and_entities/#entity-pool-pattern","title":"Entity Pool Pattern","text":"

    For entities that are frequently created and destroyed (like projectiles):

    class ProjectilePool {\n    static const int POOL_SIZE = 10;\n    ProjectileActor pool[POOL_SIZE];\n\npublic:\n    ProjectileActor* getAvailable() {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (!pool[i].isActive) {\n                pool[i].isActive = true;\n                return &pool[i];\n            }\n        }\n        return nullptr; // Pool exhausted\n    }\n};\n
    "},{"location":"manual/game_development/scenes_and_entities/#entity-factory-pattern","title":"Entity Factory Pattern","text":"

    Create entities through factory functions:

    Entity* createEnemy(EnemyType type, float x, float y) {\n    switch (type) {\n        case EnemyType::BASIC:\n            return new BasicEnemy(x, y);\n        case EnemyType::FAST:\n            return new FastEnemy(x, y);\n        // ...\n    }\n}\n
    "},{"location":"manual/game_development/scenes_and_entities/#next-steps","title":"Next Steps","text":"

    Now that you understand scenes and entities, learn about: - Basic Rendering - Draw sprites, text, and primitives - Input and Control - Handle user input - Physics and Collisions - Advanced collision handling

    See also: - Fundamental Concepts - API Reference - Scene - API Reference - Entity - API Reference - Actor

    "},{"location":"manual/game_development/user_interface/","title":"User Interface","text":"

    PixelRoot32 provides a complete UI system for creating menus, HUDs, and interface elements. This guide covers all UI components and layout systems.

    "},{"location":"manual/game_development/user_interface/#ui-elements","title":"UI Elements","text":"

    All UI elements inherit from UIElement, which itself inherits from Entity. This means UI elements can be added to scenes just like any other entity.

    "},{"location":"manual/game_development/user_interface/#uilabel","title":"UILabel","text":"

    Display text on screen:

    #include <graphics/ui/UILabel.h>\n\n// Create a label\npixelroot32::graphics::ui::UILabel* scoreLabel = new pixelroot32::graphics::ui::UILabel(\n    \"Score: 0\",                    // text\n    10,                            // x position\n    10,                            // y position\n    pixelroot32::graphics::Color::White,  // color\n    1                              // size multiplier\n);\n\n// Add to scene\naddEntity(scoreLabel);\n\n// Update text dynamically\nscoreLabel->setText(\"Score: 100\");\n\n// Center horizontally\nscoreLabel->centerX(240); // Screen width\n
    "},{"location":"manual/game_development/user_interface/#uibutton","title":"UIButton","text":"

    Create clickable buttons:

    #include <graphics/ui/UIButton.h>\n\n// Create a button\npixelroot32::graphics::ui::UIButton* startButton = new pixelroot32::graphics::ui::UIButton(\n    \"Start Game\",                  // text\n    4,                             // navigation index\n    50,                            // x position\n    100,                           // y position\n    140,                           // width\n    30,                            // height\n    []() {                         // callback function\n        // Button clicked - start game\n        startGame();\n    }\n);\n\n// Configure style\nstartButton->setStyle(\n    pixelroot32::graphics::Color::White,   // text color\n    pixelroot32::graphics::Color::Blue,    // background color\n    true                                    // draw background\n);\n\n// Add to scene\naddEntity(startButton);\n
    "},{"location":"manual/game_development/user_interface/#uicheckbox","title":"UICheckBox","text":"

    Create interactive checkboxes:

    #include <graphics/ui/UICheckBox.h>\n\n// Create a checkbox\npixelroot32::graphics::ui::UICheckBox* soundCheckbox = new pixelroot32::graphics::ui::UICheckBox(\n    \"Enable Sound\",                // text\n    4,                             // navigation index\n    50,                            // x position\n    140,                           // y position\n    140,                           // width\n    20,                            // height\n    true,                          // initial checked state\n    [](bool checked) {             // callback function\n        // Checkbox state changed\n        setSoundEnabled(checked);\n    },\n    1                              // font size\n);\n\n// Configure style\nsoundCheckbox->setStyle(\n    pixelroot32::graphics::Color::White,   // text color\n    pixelroot32::graphics::Color::Blue,    // background color\n    false                                  // draw background\n);\n\n// Add to scene\naddEntity(soundCheckbox);\n
    "},{"location":"manual/game_development/user_interface/#uipanel","title":"UIPanel","text":"

    Create visual containers with background and border:

    #include <graphics/ui/UIPanel.h>\n\n// Create a panel\npixelroot32::graphics::ui::UIPanel* dialog = new pixelroot32::graphics::ui::UIPanel(\n    50,   // x\n    50,   // y\n    140,  // width\n    140   // height\n);\n\n// Configure appearance\ndialog->setBackgroundColor(pixelroot32::graphics::Color::Black);\ndialog->setBorderColor(pixelroot32::graphics::Color::White);\ndialog->setBorderWidth(2);\n\n// Add content (typically a layout)\ndialog->setChild(menuLayout);\n\n// Add to scene\naddEntity(dialog);\n
    "},{"location":"manual/game_development/user_interface/#layouts","title":"Layouts","text":"

    Layouts automatically organize UI elements, eliminating the need for manual position calculations.

    "},{"location":"manual/game_development/user_interface/#uiverticallayout","title":"UIVerticalLayout","text":"

    Organize elements vertically with automatic scrolling:

    #include <graphics/ui/UIVerticalLayout.h>\n\n// Create vertical layout\npixelroot32::graphics::ui::UIVerticalLayout* menu = new pixelroot32::graphics::ui::UIVerticalLayout(\n    10,   // x\n    60,   // y\n    220,  // width\n    160   // height (viewport)\n);\n\n// Configure layout\nmenu->setPadding(5);        // Internal padding\nmenu->setSpacing(6);        // Space between elements\nmenu->setScrollEnabled(true); // Enable scrolling\n\n// Set navigation buttons\nmenu->setNavigationButtons(0, 1); // UP=0, DOWN=1\n\n// Set button styles\nmenu->setButtonStyle(\n    pixelroot32::graphics::Color::White,  // selected text\n    pixelroot32::graphics::Color::Cyan,    // selected background\n    pixelroot32::graphics::Color::White,  // unselected text\n    pixelroot32::graphics::Color::Black   // unselected background\n);\n\n// Add buttons (no manual positioning needed!)\nfor (int i = 0; i < 10; i++) {\n    UIButton* btn = new UIButton(\n        \"Option \" + std::to_string(i),\n        i,\n        0, 0,  // Position ignored - layout handles it\n        200, 20,\n        [i]() { handleOption(i); }\n    );\n    menu->addElement(btn);\n}\n\n// Add layout to scene\nmenu->setRenderLayer(2); // UI layer\naddEntity(menu);\n
    "},{"location":"manual/game_development/user_interface/#uihorizontallayout","title":"UIHorizontalLayout","text":"

    Organize elements horizontally:

    #include <graphics/ui/UIHorizontalLayout.h>\n\n// Create horizontal layout (menu bar)\npixelroot32::graphics::ui::UIHorizontalLayout* menuBar = new pixelroot32::graphics::ui::UIHorizontalLayout(\n    0,    // x\n    0,    // y\n    240,  // width\n    30    // height\n);\n\nmenuBar->setPadding(5);\nmenuBar->setSpacing(4);\nmenuBar->setScrollEnabled(true);\nmenuBar->setNavigationButtons(2, 3); // LEFT=2, RIGHT=3\n\n// Add menu items\nmenuBar->addElement(new UIButton(\"File\", 0, 0, 0, 60, 20, []() {}));\nmenuBar->addElement(new UIButton(\"Edit\", 1, 0, 0, 60, 20, []() {}));\nmenuBar->addElement(new UIButton(\"View\", 2, 0, 0, 60, 20, []() {}));\n\naddEntity(menuBar);\n
    "},{"location":"manual/game_development/user_interface/#uigridlayout","title":"UIGridLayout","text":"

    Organize elements in a grid (matrix):

    #include <graphics/ui/UIGridLayout.h>\n\n// Create grid layout (inventory)\npixelroot32::graphics::ui::UIGridLayout* inventory = new pixelroot32::graphics::ui::UIGridLayout(\n    10,   // x\n    60,   // y\n    220,  // width\n    160   // height\n);\n\ninventory->setColumns(4);  // 4 columns\ninventory->setPadding(5);\ninventory->setSpacing(4);\ninventory->setNavigationButtons(0, 1, 2, 3); // UP, DOWN, LEFT, RIGHT\n\n// Add items (automatically arranged in grid)\nfor (int i = 0; i < 16; i++) {\n    UIButton* item = new UIButton(\n        \"Item \" + std::to_string(i),\n        i,\n        0, 0,  // Position ignored\n        50, 50,\n        [i]() { useItem(i); }\n    );\n    inventory->addElement(item);\n}\n\naddEntity(inventory);\n
    "},{"location":"manual/game_development/user_interface/#uianchorlayout","title":"UIAnchorLayout","text":"

    Position elements at fixed screen positions (perfect for HUDs):

    #include <graphics/ui/UIAnchorLayout.h>\n\n// Create anchor layout for HUD\npixelroot32::graphics::ui::UIAnchorLayout* hud = new pixelroot32::graphics::ui::UIAnchorLayout(\n    0,    // x\n    0,    // y\n    240,  // screen width\n    240   // screen height\n);\n\nhud->setScreenSize(240, 240);\n\n// Add HUD elements at different anchor points\nUILabel* scoreLabel = new UILabel(\"Score: 0\", 0, 0, Color::White, 1);\nUILabel* livesLabel = new UILabel(\"Lives: 3\", 0, 0, Color::White, 1);\n\nhud->addElement(scoreLabel, pixelroot32::graphics::ui::Anchor::TOP_LEFT);\nhud->addElement(livesLabel, pixelroot32::graphics::ui::Anchor::TOP_RIGHT);\n\naddEntity(hud);\n

    Available Anchors: - TOP_LEFT, TOP_RIGHT, TOP_CENTER - BOTTOM_LEFT, BOTTOM_RIGHT, BOTTOM_CENTER - LEFT_CENTER, RIGHT_CENTER - CENTER

    "},{"location":"manual/game_development/user_interface/#uipaddingcontainer","title":"UIPaddingContainer","text":"

    Add padding around a single element:

    #include <graphics/ui/UIPaddingContainer.h>\n\n// Create padding container\npixelroot32::graphics::ui::UIPaddingContainer* container = new pixelroot32::graphics::ui::UIPaddingContainer(\n    10,   // x\n    10,   // y\n    200,  // width\n    100   // height\n);\n\n// Set uniform padding\ncontainer->setPadding(10);\n\n// Or set asymmetric padding\ncontainer->setPadding(5, 15, 10, 10); // left, right, top, bottom\n\n// Add child element\ncontainer->setChild(button);\n\naddEntity(container);\n
    "},{"location":"manual/game_development/user_interface/#navigation","title":"Navigation","text":"

    Layouts handle D-pad navigation automatically:

    "},{"location":"manual/game_development/user_interface/#vertical-navigation","title":"Vertical Navigation","text":"
    verticalLayout->setNavigationButtons(Buttons::UP, Buttons::DOWN);\n\n// Layout automatically:\n// - Highlights selected button\n// - Scrolls to keep selected button visible\n// - Handles wrapping (optional)\n
    "},{"location":"manual/game_development/user_interface/#horizontal-navigation","title":"Horizontal Navigation","text":"
    horizontalLayout->setNavigationButtons(Buttons::LEFT, Buttons::RIGHT);\n
    "},{"location":"manual/game_development/user_interface/#grid-navigation","title":"Grid Navigation","text":"
    gridLayout->setNavigationButtons(Buttons::UP, Buttons::DOWN, Buttons::LEFT, Buttons::RIGHT);\n\n// Layout automatically:\n// - Handles 4-direction navigation\n// - Wraps around edges\n// - Updates selection\n
    "},{"location":"manual/game_development/user_interface/#manual-selection","title":"Manual Selection","text":"
    // Set selected element programmatically\nlayout->setSelectedIndex(2);\n\n// Get selected element\nint selected = layout->getSelectedIndex();\nUIElement* element = layout->getSelectedElement();\n
    "},{"location":"manual/game_development/user_interface/#complete-example-main-menu","title":"Complete Example: Main Menu","text":"
    #include <core/Scene.h>\n#include <graphics/ui/UIVerticalLayout.h>\n#include <graphics/ui/UIButton.h>\n#include <graphics/ui/UILabel.h>\n#include <graphics/ui/UIPanel.h>\n\nclass MainMenuScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIVerticalLayout* menuLayout;\n    pixelroot32::graphics::ui::UIPanel* menuPanel;\n\npublic:\n    void init() override {\n        // Create panel\n        menuPanel = new pixelroot32::graphics::ui::UIPanel(40, 40, 160, 160);\n        menuPanel->setBackgroundColor(pixelroot32::graphics::Color::Black);\n        menuPanel->setBorderColor(pixelroot32::graphics::Color::White);\n        menuPanel->setBorderWidth(2);\n        menuPanel->setRenderLayer(2);\n        addEntity(menuPanel);\n\n        // Create layout inside panel\n        menuLayout = new pixelroot32::graphics::ui::UIVerticalLayout(0, 0, 160, 160);\n        menuLayout->setPadding(10);\n        menuLayout->setSpacing(8);\n        menuLayout->setNavigationButtons(0, 1);\n        menuLayout->setButtonStyle(\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Cyan,\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Black\n        );\n\n        // Add title\n        pixelroot32::graphics::ui::UILabel* title = new pixelroot32::graphics::ui::UILabel(\n            \"GAME MENU\", 0, 0, pixelroot32::graphics::Color::Yellow, 2\n        );\n        menuLayout->addElement(title);\n\n        // Add menu buttons\n        menuLayout->addElement(new pixelroot32::graphics::ui::UIButton(\n            \"Start Game\", 0, 0, 0, 140, 25, []() { startGame(); }\n        ));\n\n        menuLayout->addElement(new pixelroot32::graphics::ui::UIButton(\n            \"Options\", 1, 0, 0, 140, 25, []() { showOptions(); }\n        ));\n\n        menuLayout->addElement(new pixelroot32::graphics::ui::UIButton(\n            \"Quit\", 2, 0, 0, 140, 25, []() { quitGame(); }\n        ));\n\n        menuPanel->setChild(menuLayout);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Handle input for layout navigation\n        auto& input = engine.getInputManager();\n        menuLayout->handleInput(input);\n\n        Scene::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/game_development/user_interface/#complete-example-hud","title":"Complete Example: HUD","text":"
    class GameHUD : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::ui::UIAnchorLayout* hud;\n    pixelroot32::graphics::ui::UILabel* scoreLabel;\n    pixelroot32::graphics::ui::UILabel* livesLabel;\n    pixelroot32::graphics::ui::UILabel* healthLabel;\n\npublic:\n    GameHUD()\n        : Entity(0, 0, 240, 240, pixelroot32::core::EntityType::UI_ELEMENT) {\n        setRenderLayer(2); // UI layer\n\n        // Create HUD layout\n        hud = new pixelroot32::graphics::ui::UIAnchorLayout(0, 0, 240, 240);\n        hud->setScreenSize(240, 240);\n\n        // Create labels\n        scoreLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Score: 0\", 0, 0, pixelroot32::graphics::Color::White, 1\n        );\n\n        livesLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Lives: 3\", 0, 0, pixelroot32::graphics::Color::White, 1\n        );\n\n        healthLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Health: 100%\", 0, 0, pixelroot32::graphics::Color::Green, 1\n        );\n\n        // Position labels\n        hud->addElement(scoreLabel, pixelroot32::graphics::ui::Anchor::TOP_LEFT);\n        hud->addElement(livesLabel, pixelroot32::graphics::ui::Anchor::TOP_RIGHT);\n        hud->addElement(healthLabel, pixelroot32::graphics::ui::Anchor::BOTTOM_CENTER);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Update HUD text (example)\n        char buffer[32];\n        snprintf(buffer, sizeof(buffer), \"Score: %d\", currentScore);\n        scoreLabel->setText(buffer);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // HUD draws itself through its layout\n    }\n\n    // Add HUD to scene\n    void addToScene(Scene* scene) {\n        scene->addEntity(hud);\n    }\n};\n
    "},{"location":"manual/game_development/user_interface/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/user_interface/#organization","title":"Organization","text":"
    • Use render layer 2: Keep all UI on the top layer
    • Group related elements: Use panels to group menu items
    • Separate HUD from menus: Use different layouts for different UI types
    • Reuse layouts: Create layout factories for common patterns
    "},{"location":"manual/game_development/user_interface/#performance","title":"Performance","text":"
    • Layouts use viewport culling: Only visible elements are rendered
    • Minimize text updates: Updating text has overhead
    • Use appropriate layouts: Choose the right layout for your needs
    • Limit element count: Too many elements can impact performance
    "},{"location":"manual/game_development/user_interface/#navigation_1","title":"Navigation","text":"
    • Set navigation buttons: Configure D-pad navigation for layouts
    • Handle input in update(): Check for button presses to trigger actions
    • Provide visual feedback: Selected buttons should be clearly visible
    • Test navigation flow: Ensure navigation feels responsive
    "},{"location":"manual/game_development/user_interface/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/user_interface/#menu-system","title":"Menu System","text":"
    class MenuSystem {\n    UIVerticalLayout* currentMenu;\n\npublic:\n    void showMainMenu() {\n        currentMenu = createMainMenu();\n        scene->addEntity(currentMenu);\n    }\n\n    void showPauseMenu() {\n        currentMenu = createPauseMenu();\n        scene->addEntity(currentMenu);\n    }\n\n    void hideMenu() {\n        if (currentMenu) {\n            scene->removeEntity(currentMenu);\n            currentMenu = nullptr;\n        }\n    }\n};\n
    "},{"location":"manual/game_development/user_interface/#dynamic-ui-updates","title":"Dynamic UI Updates","text":"
    void updateHUD(int score, int lives, int health) {\n    char buffer[32];\n\n    snprintf(buffer, sizeof(buffer), \"Score: %d\", score);\n    scoreLabel->setText(buffer);\n\n    snprintf(buffer, sizeof(buffer), \"Lives: %d\", lives);\n    livesLabel->setText(buffer);\n\n    snprintf(buffer, sizeof(buffer), \"Health: %d%%\", health);\n    healthLabel->setText(buffer);\n}\n
    "},{"location":"manual/game_development/user_interface/#next-steps","title":"Next Steps","text":"

    Now that you understand the UI system, you've completed the core game development topics. Continue with: - Advanced Graphics - Advanced sprite techniques - Camera and Scrolling - Create scrolling levels - Performance Tuning - Improve performance

    See also: - API Reference - UIElement - API Reference - UIButton - API Reference - UI Layouts - Manual - UI Overview

    "},{"location":"manual/optimization/extensibility/","title":"Extensibility","text":"

    PixelRoot32 is designed to be extensible. This guide covers how to create custom drivers, audio backends, and extend existing systems.

    "},{"location":"manual/optimization/extensibility/#creating-custom-display-drivers","title":"Creating Custom Display Drivers","text":"

    To support a new display, implement the DrawSurface interface.

    "},{"location":"manual/optimization/extensibility/#drawsurface-interface","title":"DrawSurface Interface","text":"
    #include <graphics/DrawSurface.h>\n\nclass MyCustomDrawer : public pixelroot32::graphics::DrawSurface {\npublic:\n    // Required methods\n    void init() override;\n    void setRotation(uint8_t rotation) override;\n    void clearBuffer() override;\n    void sendBuffer() override;\n\n    // Drawing primitives\n    void drawPixel(int x, int y, uint16_t color) override;\n    void drawLine(int x1, int y1, int x2, int y2, uint16_t color) override;\n    void drawRectangle(int x, int y, int width, int height, uint16_t color) override;\n    void drawFilledRectangle(int x, int y, int width, int height, uint16_t color) override;\n    void drawCircle(int x, int y, int radius, uint16_t color) override;\n    void drawFilledCircle(int x, int y, int radius, uint16_t color) override;\n    void drawBitmap(int x, int y, int width, int height, const uint8_t* bitmap, uint16_t color) override;\n\n    // Text (deprecated, but must implement)\n    void drawText(const char* text, int16_t x, int16_t y, uint16_t color, uint8_t size) override;\n    void drawTextCentered(const char* text, int16_t y, uint16_t color, uint8_t size) override;\n\n    // State management\n    void setTextColor(uint16_t color) override;\n    void setTextSize(uint8_t size) override;\n    void setCursor(int16_t x, int16_t y) override;\n    void setContrast(uint8_t level) override;\n    void setDisplaySize(int w, int h) override;\n\n    // Utilities\n    uint16_t color565(uint8_t r, uint8_t g, uint8_t b) override;\n    bool processEvents() override;\n    void present() override;\n};\n
    "},{"location":"manual/optimization/extensibility/#example-simple-custom-drawer","title":"Example: Simple Custom Drawer","text":"
    #include <graphics/DrawSurface.h>\n\nclass SimpleDrawer : public pixelroot32::graphics::DrawSurface {\nprivate:\n    uint16_t* framebuffer;\n    int width, height;\n\npublic:\n    SimpleDrawer(int w, int h) : width(w), height(h) {\n        framebuffer = new uint16_t[w * h];\n    }\n\n    ~SimpleDrawer() {\n        delete[] framebuffer;\n    }\n\n    void init() override {\n        // Initialize your display hardware\n        // Clear framebuffer\n        clearBuffer();\n    }\n\n    void clearBuffer() override {\n        for (int i = 0; i < width * height; i++) {\n            framebuffer[i] = 0x0000; // Black\n        }\n    }\n\n    void sendBuffer() override {\n        // Send framebuffer to display\n        // Implementation depends on your hardware\n    }\n\n    void drawPixel(int x, int y, uint16_t color) override {\n        if (x >= 0 && x < width && y >= 0 && y < height) {\n            framebuffer[y * width + x] = color;\n        }\n    }\n\n    void drawFilledRectangle(int x, int y, int w, int h, uint16_t color) override {\n        for (int py = y; py < y + h; py++) {\n            for (int px = x; px < x + w; px++) {\n                drawPixel(px, py, color);\n            }\n        }\n    }\n\n    // Implement other required methods...\n    // (See TFT_eSPI_Drawer or SDL2_Drawer for reference implementations)\n};\n
    "},{"location":"manual/optimization/extensibility/#integrating-custom-driver","title":"Integrating Custom Driver","text":"
    // Create custom drawer\nSimpleDrawer* customDrawer = new SimpleDrawer(240, 240);\n\n// Create renderer with custom drawer\npixelroot32::graphics::DisplayConfig config(\n    pixelroot32::graphics::DisplayType::NONE, // Use NONE for custom\n    0, 240, 240\n);\n\n// You'll need to modify Engine to accept custom DrawSurface\n// Or create a custom Engine wrapper\n
    "},{"location":"manual/optimization/extensibility/#creating-custom-audio-backends","title":"Creating Custom Audio Backends","text":"

    Implement the AudioBackend interface for custom audio hardware.

    "},{"location":"manual/optimization/extensibility/#audiobackend-interface","title":"AudioBackend Interface","text":"
    #include <audio/AudioBackend.h>\n\nclass MyCustomAudioBackend : public pixelroot32::audio::AudioBackend {\npublic:\n    // Required methods\n    void init() override;\n    void start() override;\n    void stop() override;\n    uint32_t getSampleRate() const override;\n\n    // Audio generation\n    int16_t generateSample() override;\n\n    // Channel management\n    void setChannelWave(int channel, pixelroot32::audio::WaveType type, float frequency, float duty) override;\n    void setChannelVolume(int channel, float volume) override;\n    void stopChannel(int channel) override;\n};\n
    "},{"location":"manual/optimization/extensibility/#example-custom-audio-backend","title":"Example: Custom Audio Backend","text":"
    #include <audio/AudioBackend.h>\n\nclass CustomAudioBackend : public pixelroot32::audio::AudioBackend {\nprivate:\n    uint32_t sampleRate;\n    float phase[4] = {0, 0, 0, 0}; // 4 channels\n    float frequency[4] = {0, 0, 0, 0};\n    float volume[4] = {0, 0, 0, 0};\n    pixelroot32::audio::WaveType waveType[4];\n\npublic:\n    CustomAudioBackend(uint32_t rate) : sampleRate(rate) {\n        for (int i = 0; i < 4; i++) {\n            waveType[i] = pixelroot32::audio::WaveType::PULSE;\n            volume[i] = 0.0f;\n        }\n    }\n\n    void init() override {\n        // Initialize your audio hardware\n    }\n\n    void start() override {\n        // Start audio output\n    }\n\n    void stop() override {\n        // Stop audio output\n    }\n\n    uint32_t getSampleRate() const override {\n        return sampleRate;\n    }\n\n    int16_t generateSample() override {\n        float sample = 0.0f;\n\n        for (int ch = 0; ch < 4; ch++) {\n            if (frequency[ch] > 0 && volume[ch] > 0) {\n                float phaseIncrement = frequency[ch] / sampleRate;\n                phase[ch] += phaseIncrement;\n                if (phase[ch] >= 1.0f) phase[ch] -= 1.0f;\n\n                float channelSample = 0.0f;\n                switch (waveType[ch]) {\n                    case pixelroot32::audio::WaveType::PULSE:\n                        channelSample = (phase[ch] < 0.5f) ? 1.0f : -1.0f;\n                        break;\n                    case pixelroot32::audio::WaveType::TRIANGLE:\n                        channelSample = (phase[ch] < 0.5f) ? \n                            (phase[ch] * 4.0f - 1.0f) : \n                            (3.0f - phase[ch] * 4.0f);\n                        break;\n                    // ... other wave types\n                }\n\n                sample += channelSample * volume[ch];\n            }\n        }\n\n        // Clamp and convert to int16_t\n        if (sample > 1.0f) sample = 1.0f;\n        if (sample < -1.0f) sample = -1.0f;\n        return static_cast<int16_t>(sample * 32767.0f);\n    }\n\n    void setChannelWave(int ch, pixelroot32::audio::WaveType type, \n                       float freq, float duty) override {\n        if (ch >= 0 && ch < 4) {\n            waveType[ch] = type;\n            frequency[ch] = freq;\n        }\n    }\n\n    void setChannelVolume(int ch, float vol) override {\n        if (ch >= 0 && ch < 4) {\n            volume[ch] = vol;\n        }\n    }\n\n    void stopChannel(int ch) override {\n        if (ch >= 0 && ch < 4) {\n            frequency[ch] = 0.0f;\n            volume[ch] = 0.0f;\n        }\n    }\n};\n
    "},{"location":"manual/optimization/extensibility/#extending-existing-systems","title":"Extending Existing Systems","text":""},{"location":"manual/optimization/extensibility/#custom-entity-types","title":"Custom Entity Types","text":"

    Create specialized entity types:

    class PowerUpActor : public pixelroot32::core::Actor {\nprivate:\n    PowerUpType type;\n    float lifetime = 5.0f; // 5 seconds\n\npublic:\n    PowerUpActor(float x, float y, PowerUpType t)\n        : Actor(x, y, 8, 8), type(t) {\n        setRenderLayer(1);\n        setCollisionLayer(Layers::POWERUP);\n        setCollisionMask(Layers::PLAYER);\n    }\n\n    void update(unsigned long deltaTime) override {\n        lifetime -= deltaTime * 0.001f;\n        if (lifetime <= 0) {\n            isEnabled = false;\n            isVisible = false;\n        }\n\n        // Animate (bob up and down)\n        y += sin(millis() * 0.005f) * 0.5f;\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        if (other->isInLayer(Layers::PLAYER)) {\n            applyPowerUp(other);\n            isEnabled = false;\n            isVisible = false;\n        }\n    }\n\nprivate:\n    void applyPowerUp(pixelroot32::core::Actor* player) {\n        switch (type) {\n            case PowerUpType::SPEED:\n                // Increase player speed\n                break;\n            case PowerUpType::HEALTH:\n                // Restore health\n                break;\n            // ...\n        }\n    }\n};\n
    "},{"location":"manual/optimization/extensibility/#custom-ui-layouts","title":"Custom UI Layouts","text":"

    Create new layout types:

    #include <graphics/ui/UILayout.h>\n\nclass UICircularLayout : public pixelroot32::graphics::ui::UILayout {\nprivate:\n    float radius;\n    float startAngle;\n\npublic:\n    UICircularLayout(float x, float y, float w, float h, float r)\n        : UILayout(x, y, w, h), radius(r), startAngle(0.0f) {\n    }\n\n    void updateLayout() override {\n        int count = elements.size();\n        float angleStep = 360.0f / count;\n\n        for (size_t i = 0; i < elements.size(); i++) {\n            float angle = startAngle + (i * angleStep);\n            float rad = angle * M_PI / 180.0f;\n\n            float elementX = x + (radius * cos(rad)) - (elements[i]->width / 2);\n            float elementY = y + (radius * sin(rad)) - (elements[i]->height / 2);\n\n            elements[i]->x = elementX;\n            elements[i]->y = elementY;\n        }\n    }\n\n    void handleInput(const pixelroot32::input::InputManager& input) override {\n        // Implement circular navigation\n        // ...\n    }\n};\n
    "},{"location":"manual/optimization/extensibility/#custom-collision-primitives","title":"Custom Collision Primitives","text":"

    Extend collision system with new shapes:

    // Add to your game code (not engine modification)\nstruct Triangle {\n    float x1, y1, x2, y2, x3, y3;\n};\n\nbool intersects(const Triangle& tri, const pixelroot32::core::Rect& rect) {\n    // Implement triangle-rectangle intersection\n    // ...\n    return false;\n}\n\nbool intersects(const Triangle& tri1, const Triangle& tri2) {\n    // Implement triangle-triangle intersection\n    // ...\n    return false;\n}\n
    "},{"location":"manual/optimization/extensibility/#best-practices","title":"Best Practices","text":""},{"location":"manual/optimization/extensibility/#maintain-compatibility","title":"Maintain Compatibility","text":"
    • Don't break existing APIs: Extend, don't modify
    • Use inheritance: Inherit from base classes
    • Follow patterns: Match existing code patterns
    • Document extensions: Comment your custom code
    "},{"location":"manual/optimization/extensibility/#testing","title":"Testing","text":"
    • Test on both platforms: ESP32 and Native
    • Test edge cases: Boundary conditions, null pointers
    • Performance testing: Ensure extensions don't hurt performance
    • Memory testing: Check for leaks with custom code
    "},{"location":"manual/optimization/extensibility/#documentation","title":"Documentation","text":"
    • Comment your code: Explain why, not just what
    • Provide examples: Show how to use your extensions
    • Document limitations: State what doesn't work
    • Version compatibility: Note which engine version
    "},{"location":"manual/optimization/extensibility/#common-extension-patterns","title":"Common Extension Patterns","text":""},{"location":"manual/optimization/extensibility/#factory-pattern","title":"Factory Pattern","text":"
    class EntityFactory {\npublic:\n    static pixelroot32::core::Entity* createEnemy(EnemyType type, float x, float y) {\n        switch (type) {\n            case EnemyType::BASIC:\n                return new BasicEnemy(x, y);\n            case EnemyType::FAST:\n                return new FastEnemy(x, y);\n            case EnemyType::TANK:\n                return new TankEnemy(x, y);\n            default:\n                return nullptr;\n        }\n    }\n\n    static pixelroot32::core::Entity* createPowerUp(PowerUpType type, float x, float y) {\n        return new PowerUpActor(x, y, type);\n    }\n};\n
    "},{"location":"manual/optimization/extensibility/#strategy-pattern","title":"Strategy Pattern","text":"
    class MovementStrategy {\npublic:\n    virtual void update(pixelroot32::core::Actor* actor, unsigned long deltaTime) = 0;\n};\n\nclass LinearMovement : public MovementStrategy {\npublic:\n    void update(pixelroot32::core::Actor* actor, unsigned long deltaTime) override {\n        actor->x += speed * (deltaTime * 0.001f);\n    }\n};\n\nclass CircularMovement : public MovementStrategy {\npublic:\n    void update(pixelroot32::core::Actor* actor, unsigned long deltaTime) override {\n        float angle = millis() * 0.001f;\n        actor->x = centerX + radius * cos(angle);\n        actor->y = centerY + radius * sin(angle);\n    }\n};\n\nclass SmartEnemy : public pixelroot32::core::Actor {\nprivate:\n    MovementStrategy* movement;\n\npublic:\n    void setMovement(MovementStrategy* strat) {\n        movement = strat;\n    }\n\n    void update(unsigned long deltaTime) override {\n        if (movement) {\n            movement->update(this, deltaTime);\n        }\n    }\n};\n
    "},{"location":"manual/optimization/extensibility/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/optimization/extensibility/#driver-not-working","title":"Driver Not Working","text":"
    • Verify all interface methods are implemented
    • Check initialization order
    • Test with simple drawing first
    • Verify hardware connections
    "},{"location":"manual/optimization/extensibility/#audio-backend-issues","title":"Audio Backend Issues","text":"
    • Check sample rate matches hardware
    • Verify audio generation logic
    • Test with simple tones first
    • Check channel management
    "},{"location":"manual/optimization/extensibility/#extension-conflicts","title":"Extension Conflicts","text":"
    • Ensure namespace isolation
    • Avoid modifying engine code directly
    • Use composition over modification
    • Test with engine updates
    "},{"location":"manual/optimization/extensibility/#next-steps","title":"Next Steps","text":"

    Now that you understand extensibility, you've completed the optimization section. Continue with: - API Reference - Complete API documentation - Examples - Code examples - Resources - Tools and troubleshooting

    See also: - API Reference - DrawSurface - API Reference - AudioBackend - Manual - Platforms and Drivers

    "},{"location":"manual/optimization/memory_management/","title":"Memory Management","text":"

    ESP32 has limited memory, so efficient memory management is crucial for PixelRoot32 games. This guide covers memory constraints, object pooling, and best practices.

    "},{"location":"manual/optimization/memory_management/#esp32-memory-constraints","title":"ESP32 Memory Constraints","text":""},{"location":"manual/optimization/memory_management/#available-memory","title":"Available Memory","text":"

    ESP32 typically has: - RAM: ~320KB total (varies by model) - Flash: 4MB+ (for program storage) - Heap: Limited and fragmented over time

    "},{"location":"manual/optimization/memory_management/#real-world-limits","title":"Real-World Limits","text":"
    • MAX_ENTITIES: 32 per scene (hard limit)
    • Sprite data: Stored in flash (const/constexpr)
    • Dynamic allocation: Should be avoided in game loop
    • Stack: Limited (~8KB), avoid large stack allocations
    "},{"location":"manual/optimization/memory_management/#object-pooling","title":"Object Pooling","text":"

    Object pooling reuses objects instead of creating/destroying them, avoiding memory fragmentation.

    "},{"location":"manual/optimization/memory_management/#basic-pool-pattern","title":"Basic Pool Pattern","text":"
    class ProjectilePool {\nprivate:\n    static const int POOL_SIZE = 10;\n    ProjectileActor pool[POOL_SIZE];\n    bool inUse[POOL_SIZE];\n\npublic:\n    ProjectilePool() {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            inUse[i] = false;\n        }\n    }\n\n    ProjectileActor* getAvailable() {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (!inUse[i]) {\n                inUse[i] = true;\n                return &pool[i];\n            }\n        }\n        return nullptr; // Pool exhausted\n    }\n\n    void release(ProjectileActor* projectile) {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (&pool[i] == projectile) {\n                inUse[i] = false;\n                projectile->isEnabled = false;\n                projectile->isVisible = false;\n                break;\n            }\n        }\n    }\n};\n
    "},{"location":"manual/optimization/memory_management/#using-object-pools","title":"Using Object Pools","text":"
    class GameScene : public pixelroot32::core::Scene {\nprivate:\n    ProjectilePool projectilePool;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Fire projectile\n        if (input.isButtonPressed(Buttons::A)) {\n            ProjectileActor* proj = projectilePool.getAvailable();\n            if (proj) {\n                proj->x = player->x;\n                proj->y = player->y;\n                proj->isEnabled = true;\n                proj->isVisible = true;\n                // ... initialize projectile\n            }\n        }\n\n        // Clean up projectiles that hit target\n        for (auto* entity : entities) {\n            if (auto* proj = dynamic_cast<ProjectileActor*>(entity)) {\n                if (proj->hitTarget) {\n                    projectilePool.release(proj);\n                }\n            }\n        }\n    }\n};\n
    "},{"location":"manual/optimization/memory_management/#complete-example-entity-pool","title":"Complete Example: Entity Pool","text":"
    template<typename T, int POOL_SIZE>\nclass EntityPool {\nprivate:\n    T pool[POOL_SIZE];\n    bool inUse[POOL_SIZE];\n    int activeCount = 0;\n\npublic:\n    EntityPool() {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            inUse[i] = false;\n        }\n    }\n\n    T* acquire() {\n        if (activeCount >= POOL_SIZE) {\n            return nullptr; // Pool full\n        }\n\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (!inUse[i]) {\n                inUse[i] = true;\n                activeCount++;\n                return &pool[i];\n            }\n        }\n        return nullptr;\n    }\n\n    void release(T* obj) {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (&pool[i] == obj) {\n                inUse[i] = false;\n                activeCount--;\n                obj->isEnabled = false;\n                obj->isVisible = false;\n                break;\n            }\n        }\n    }\n\n    int getActiveCount() const { return activeCount; }\n    int getAvailableCount() const { return POOL_SIZE - activeCount; }\n};\n\n// Usage\nEntityPool<EnemyActor, 8> enemyPool;\nEntityPool<ParticleEmitter, 5> particlePool;\n
    "},{"location":"manual/optimization/memory_management/#scene-arena-experimental","title":"Scene Arena (Experimental)","text":"

    Scene Arena provides a memory arena for scene-specific allocations, reducing fragmentation.

    "},{"location":"manual/optimization/memory_management/#what-is-scene-arena","title":"What is Scene Arena?","text":"

    Scene Arena is a contiguous memory block pre-allocated for a scene. All scene entities are allocated from this arena instead of the heap.

    "},{"location":"manual/optimization/memory_management/#when-to-use","title":"When to Use","text":"
    • Large scenes: Scenes with many entities
    • Frequent allocation: Scenes that create/destroy entities often
    • Memory fragmentation: When heap fragmentation is a problem
    • Performance: When you need predictable allocation performance
    "},{"location":"manual/optimization/memory_management/#configuration","title":"Configuration","text":"
    #ifdef PIXELROOT32_ENABLE_SCENE_ARENA\n#include <core/Scene.h>\n\n// Define arena buffer (typically in scene header)\nstatic unsigned char MY_SCENE_ARENA_BUFFER[8192]; // 8KB arena\n\nclass MyScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Initialize arena\n        arena.init(MY_SCENE_ARENA_BUFFER, sizeof(MY_SCENE_ARENA_BUFFER));\n\n        // Now entities allocated with arena will use this memory\n        // (Requires custom allocation functions)\n    }\n};\n#endif\n
    "},{"location":"manual/optimization/memory_management/#limitations","title":"Limitations","text":"
    • Experimental: May have bugs or limitations
    • Fixed size: Arena size must be determined at compile time
    • No reallocation: Can't resize arena at runtime
    • Manual management: Requires careful memory management

    Note: Scene Arena is an experimental feature. Use object pooling for most cases.

    "},{"location":"manual/optimization/memory_management/#best-practices","title":"Best Practices","text":""},{"location":"manual/optimization/memory_management/#avoid-dynamic-allocation-in-game-loop","title":"Avoid Dynamic Allocation in Game Loop","text":"
    // \u274c BAD: Allocates every frame\nvoid update(unsigned long deltaTime) override {\n    if (shouldSpawnEnemy) {\n        EnemyActor* enemy = new EnemyActor(x, y);\n        addEntity(enemy);\n    }\n}\n\n// \u2705 GOOD: Use pool\nvoid update(unsigned long deltaTime) override {\n    if (shouldSpawnEnemy) {\n        EnemyActor* enemy = enemyPool.getAvailable();\n        if (enemy) {\n            enemy->reset(x, y);\n            enemy->isEnabled = true;\n        }\n    }\n}\n
    "},{"location":"manual/optimization/memory_management/#pre-allocate-resources","title":"Pre-allocate Resources","text":"
    class GameScene : public pixelroot32::core::Scene {\nprivate:\n    // Pre-allocated pools\n    ProjectilePool projectiles;\n    EnemyPool enemies;\n    ParticlePool particles;\n\npublic:\n    void init() override {\n        // All pools created in constructor\n        // No allocation in init() or update()\n    }\n};\n
    "},{"location":"manual/optimization/memory_management/#reuse-objects","title":"Reuse Objects","text":"
    class EnemyActor : public pixelroot32::core::Actor {\npublic:\n    void reset(float x, float y) {\n        this->x = x;\n        this->y = y;\n        this->isEnabled = true;\n        this->isVisible = true;\n        this->health = maxHealth;\n        // Reset all state\n    }\n\n    void deactivate() {\n        isEnabled = false;\n        isVisible = false;\n    }\n};\n\n// Usage\nEnemyActor* enemy = enemyPool.getAvailable();\nif (enemy) {\n    enemy->reset(spawnX, spawnY);\n    addEntity(enemy);\n}\n
    "},{"location":"manual/optimization/memory_management/#avoid-strings-and-dynamic-memory","title":"Avoid Strings and Dynamic Memory","text":"
    // \u274c BAD: String allocation\nvoid draw(Renderer& renderer) override {\n    std::string scoreText = \"Score: \" + std::to_string(score);\n    renderer.drawText(scoreText.c_str(), 10, 10, Color::White, 1);\n}\n\n// \u2705 GOOD: Static buffer\nvoid draw(Renderer& renderer) override {\n    char scoreBuffer[32];\n    snprintf(scoreBuffer, sizeof(scoreBuffer), \"Score: %d\", score);\n    renderer.drawText(scoreBuffer, 10, 10, Color::White, 1);\n}\n
    "},{"location":"manual/optimization/memory_management/#store-data-in-flash","title":"Store Data in Flash","text":"
    // \u2705 GOOD: Stored in flash (const/constexpr)\nstatic const uint16_t SPRITE_DATA[] = {\n    0b00111100,\n    0b01111110,\n    // ...\n};\n\n// \u274c BAD: Stored in RAM\nuint16_t spriteData[] = {\n    0b00111100,\n    0b01111110,\n    // ...\n};\n
    "},{"location":"manual/optimization/memory_management/#memory-monitoring","title":"Memory Monitoring","text":""},{"location":"manual/optimization/memory_management/#check-available-memory","title":"Check Available Memory","text":"
    #ifdef PLATFORM_ESP32\n#include <Arduino.h>\n\nvoid checkMemory() {\n    Serial.print(\"Free heap: \");\n    Serial.println(ESP.getFreeHeap());\n    Serial.print(\"Largest free block: \");\n    Serial.println(ESP.getMaxAllocHeap());\n}\n#endif\n
    "},{"location":"manual/optimization/memory_management/#monitor-entity-count","title":"Monitor Entity Count","text":"
    void update(unsigned long deltaTime) override {\n    Scene::update(deltaTime);\n\n    // Check entity count\n    int entityCount = getEntityCount();\n    if (entityCount >= MAX_ENTITIES) {\n        Serial.println(\"WARNING: Entity limit reached!\");\n    }\n}\n
    "},{"location":"manual/optimization/memory_management/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/optimization/memory_management/#entity-lifecycle-management","title":"Entity Lifecycle Management","text":"
    class ManagedEntity {\nprivate:\n    bool isActive = false;\n\npublic:\n    void activate(float x, float y) {\n        this->x = x;\n        this->y = y;\n        isActive = true;\n        isEnabled = true;\n        isVisible = true;\n    }\n\n    void deactivate() {\n        isActive = false;\n        isEnabled = false;\n        isVisible = false;\n    }\n\n    bool getIsActive() const { return isActive; }\n};\n\n// Pool manages lifecycle\nclass EntityManager {\nprivate:\n    EntityPool<ManagedEntity, 20> pool;\n\npublic:\n    ManagedEntity* spawn(float x, float y) {\n        auto* entity = pool.acquire();\n        if (entity) {\n            entity->activate(x, y);\n        }\n        return entity;\n    }\n\n    void despawn(ManagedEntity* entity) {\n        if (entity) {\n            entity->deactivate();\n            pool.release(entity);\n        }\n    }\n};\n
    "},{"location":"manual/optimization/memory_management/#memory-efficient-collections","title":"Memory-Efficient Collections","text":"
    // Fixed-size array instead of vector\nclass EntityArray {\nprivate:\n    static const int MAX_SIZE = 32;\n    pixelroot32::core::Entity* entities[MAX_SIZE];\n    int count = 0;\n\npublic:\n    bool add(pixelroot32::core::Entity* entity) {\n        if (count >= MAX_SIZE) return false;\n        entities[count++] = entity;\n        return true;\n    }\n\n    void remove(pixelroot32::core::Entity* entity) {\n        for (int i = 0; i < count; i++) {\n            if (entities[i] == entity) {\n                entities[i] = entities[--count];\n                break;\n            }\n        }\n    }\n\n    int size() const { return count; }\n    pixelroot32::core::Entity* operator[](int index) { return entities[index]; }\n};\n
    "},{"location":"manual/optimization/memory_management/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/optimization/memory_management/#out-of-memory-errors","title":"Out of Memory Errors","text":"
    • Reduce pool sizes
    • Use fewer entities
    • Store more data in flash
    • Avoid dynamic allocation
    • Check for memory leaks
    "},{"location":"manual/optimization/memory_management/#entity-limit-reached","title":"Entity Limit Reached","text":"
    • MAX_ENTITIES = 32 is a hard limit
    • Use object pooling to reuse entities
    • Deactivate entities instead of removing
    • Combine multiple entities into one
    "},{"location":"manual/optimization/memory_management/#memory-fragmentation","title":"Memory Fragmentation","text":"
    • Use object pooling
    • Pre-allocate all resources
    • Avoid frequent new/delete
    • Consider Scene Arena (experimental)
    "},{"location":"manual/optimization/memory_management/#next-steps","title":"Next Steps","text":"

    Now that you understand memory management, learn about: - Performance Optimization - Improve game performance - Platforms and Drivers - Understand platform specifics - Extensibility - Extend the engine

    See also: - API Reference - Scene - Manual - Scenes and Entities

    "},{"location":"manual/optimization/performance_tuning/","title":"Performance Optimization","text":"

    This guide covers techniques to improve game performance on ESP32, including rendering optimization, logic optimization, and profiling.

    "},{"location":"manual/optimization/performance_tuning/#esp32-performance-characteristics","title":"ESP32 Performance Characteristics","text":""},{"location":"manual/optimization/performance_tuning/#cpu-limitations","title":"CPU Limitations","text":"
    • Dual-core: 240MHz (typically)
    • Single-threaded game loop: One core handles everything
    • Target FPS: 30-60 FPS (depends on game complexity)
    • Frame budget: ~16-33ms per frame at 60 FPS
    "},{"location":"manual/optimization/performance_tuning/#common-bottlenecks","title":"Common Bottlenecks","text":"
    1. Rendering: Too many draw calls
    2. Collision detection: Too many collision checks
    3. Memory allocation: Dynamic allocation in game loop
    4. Complex calculations: Expensive math operations
    5. String operations: String concatenation/formatting
    "},{"location":"manual/optimization/performance_tuning/#tecnicas-de-optimizacion","title":"T\u00e9cnicas de Optimizaci\u00f3n","text":"

    El motor utiliza varias t\u00e9cnicas para maximizar los FPS, especialmente en hardware limitado como el ESP32.

    "},{"location":"manual/optimization/performance_tuning/#1-viewport-culling-recorte-de-camara","title":"1. Viewport Culling (Recorte de C\u00e1mara)","text":"

    No proceses objetos que est\u00e1n fuera de la pantalla. El motor lo hace autom\u00e1ticamente en drawTileMap, pero debes implementarlo en tu l\u00f3gica de actualizaci\u00f3n:

    bool isOnScreen(float x, float y, int width, int height, \n                const Camera2D& camera) {\n    float cameraX = camera.getX();\n    float cameraY = camera.getY();\n    int screenWidth = engine.getRenderer().getWidth();\n    int screenHeight = engine.getRenderer().getHeight();\n\n    return !(x + width < cameraX || \n             x > cameraX + screenWidth ||\n             y + height < cameraY || \n             y > cameraY + screenHeight);\n}\n
    "},{"location":"manual/optimization/performance_tuning/#2-optimizacion-de-memoria-y-cpu-esp32","title":"2. Optimizaci\u00f3n de Memoria y CPU (ESP32)","text":"

    Para la plataforma ESP32, se han implementado optimizaciones de bajo nivel cr\u00edticas:

    • IRAM_ATTR: Las funciones cr\u00edticas de renderizado (drawSprite, drawTileMap, etc.) est\u00e1n marcadas para ejecutarse desde la RAM interna (IRAM), eliminando la latencia de lectura de la Flash SPI.
    • DMA (Direct Memory Access): El volcado del buffer a la pantalla TFT se realiza mediante DMA, lo que permite que la CPU comience a procesar el siguiente frame mientras el hardware transfiere los datos.
    • Acceso a Datos de 16 bits: Los sprites de 2bpp y 4bpp utilizan punteros uint16_t* para garantizar accesos alineados a memoria, lo cual es significativamente m\u00e1s r\u00e1pido en la arquitectura Xtensa del ESP32.
    "},{"location":"manual/optimization/performance_tuning/#3-optimizacion-de-tilemaps","title":"3. Optimizaci\u00f3n de TileMaps","text":"

    El renderizado de mapas de tiles es una de las operaciones m\u00e1s costosas. PixelRoot32 utiliza:

    • Cach\u00e9 de Paleta: Durante el dibujado de un tilemap, se genera una tabla de b\u00fasqueda (LUT) temporal para evitar c\u00e1lculos de color redundantes por cada p\u00edxel.
    • Dibujado por Columnas: Optimizado para minimizar los saltos de memoria en el framebuffer.
    "},{"location":"manual/optimization/performance_tuning/#4-colisiones-eficientes","title":"4. Colisiones Eficientes","text":"

    Usa colisiones basadas en tiles siempre que sea posible. Acceder a un array de tiles es O(1), mientras que iterar sobre una lista de entidades es O(n).

    // Ejemplo de colisi\u00f3n r\u00e1pida con el mapa\nint tileX = x / 8;\nint tileY = y / 8;\nif (levelMap.data[tileY * levelMap.width + tileX] != 0) {\n    // Colisi\u00f3n detectada\n}\n
    "},{"location":"manual/optimization/performance_tuning/#recomendaciones-generales","title":"Recomendaciones Generales","text":"
    • Sprites Indexados: Prefiere Sprite2bpp (4 colores) o Sprite4bpp (16 colores) sobre Sprite (1bpp) si necesitas color, ya que est\u00e1n altamente optimizados.
    • Evitar std::string en el Loop: Las concatenaciones de strings generan fragmentaci\u00f3n de memoria. Usa buffers est\u00e1ticos o char[] para textos din\u00e1micos.
    • Perfilado: Utiliza engine.getFPS() para monitorear el impacto de tus cambios en tiempo real.
    "},{"location":"manual/optimization/performance_tuning/#common-optimization-patterns","title":"Common Optimization Patterns","text":""},{"location":"manual/optimization/performance_tuning/#update-frequency-reduction","title":"Update Frequency Reduction","text":"
    class LowFrequencyUpdater {\nprivate:\n    unsigned long timer = 0;\n    unsigned long interval = 100; // Update every 100ms\n\npublic:\n    void update(unsigned long deltaTime) {\n        timer += deltaTime;\n        if (timer >= interval) {\n            timer -= interval;\n            // Do expensive update\n            expensiveUpdate();\n        }\n    }\n};\n
    "},{"location":"manual/optimization/performance_tuning/#spatial-partitioning-simple","title":"Spatial Partitioning (Simple)","text":"
    // Divide screen into zones\nclass SpatialGrid {\nprivate:\n    static const int GRID_SIZE = 4;\n    static const int CELL_WIDTH = 60;\n    static const int CELL_HEIGHT = 60;\n\n    std::vector<Actor*> grid[GRID_SIZE][GRID_SIZE];\n\npublic:\n    void add(Actor* actor) {\n        int cellX = static_cast<int>(actor->x) / CELL_WIDTH;\n        int cellY = static_cast<int>(actor->y) / CELL_HEIGHT;\n        if (cellX >= 0 && cellX < GRID_SIZE && \n            cellY >= 0 && cellY < GRID_SIZE) {\n            grid[cellY][cellX].push_back(actor);\n        }\n    }\n\n    void checkCollisions() {\n        // Only check collisions within same cell\n        for (int y = 0; y < GRID_SIZE; y++) {\n            for (int x = 0; x < GRID_SIZE; x++) {\n                auto& cell = grid[y][x];\n                for (size_t i = 0; i < cell.size(); i++) {\n                    for (size_t j = i + 1; j < cell.size(); j++) {\n                        checkCollision(cell[i], cell[j]);\n                    }\n                }\n            }\n        }\n    }\n};\n
    "},{"location":"manual/optimization/performance_tuning/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/optimization/performance_tuning/#low-fps","title":"Low FPS","text":"
    • Profile to find bottlenecks
    • Reduce entity count
    • Optimize rendering (culling, batching)
    • Simplify collision detection
    • Reduce update frequency
    "},{"location":"manual/optimization/performance_tuning/#frame-drops","title":"Frame Drops","text":"
    • Check for expensive operations in update()
    • Avoid dynamic allocation
    • Cache calculations
    • Reduce draw calls
    "},{"location":"manual/optimization/performance_tuning/#stuttering","title":"Stuttering","text":"
    • Ensure frame-rate independence (use deltaTime)
    • Avoid blocking operations
    • Pre-load resources
    • Use object pooling
    "},{"location":"manual/optimization/performance_tuning/#next-steps","title":"Next Steps","text":"

    Now that you understand performance optimization, learn about: - Memory Management - Manage memory efficiently - Platforms and Drivers - Platform-specific optimizations - Extensibility - Extend the engine

    See also: - Manual - Basic Rendering - Manual - Physics and Collisions

    "},{"location":"manual/optimization/platforms_and_drivers/","title":"Platforms and Drivers","text":"

    PixelRoot32 supports multiple platforms through driver abstraction. This guide covers supported platforms, display drivers, audio backends, and build configuration.

    "},{"location":"manual/optimization/platforms_and_drivers/#supported-platforms","title":"Supported Platforms","text":""},{"location":"manual/optimization/platforms_and_drivers/#esp32","title":"ESP32","text":"

    Primary platform for PixelRoot32 games.

    Characteristics: - TFT_eSPI display driver - Internal DAC or I2S audio - GPIO button input - Limited RAM/Flash - Real hardware constraints

    Use for: - Final game deployment - Hardware testing - Production builds

    "},{"location":"manual/optimization/platforms_and_drivers/#nativedesktop-sdl2","title":"Native/Desktop (SDL2)","text":"

    Development platform for rapid iteration.

    Characteristics: - SDL2 display driver - SDL2 audio backend - Keyboard input - Unlimited resources (for testing) - Fast development cycle

    Use for: - Development and debugging - Testing without hardware - Rapid prototyping - CI/CD testing

    "},{"location":"manual/optimization/platforms_and_drivers/#display-drivers","title":"Display Drivers","text":""},{"location":"manual/optimization/platforms_and_drivers/#tft_espi-esp32","title":"TFT_eSPI (ESP32)","text":"

    TFT_eSPI is the display driver for ESP32, supporting many TFT displays.

    "},{"location":"manual/optimization/platforms_and_drivers/#optimizaciones-esp32","title":"Optimizaciones ESP32","text":"

    Para maximizar el rendimiento en ESP32, PixelRoot32 utiliza:

    • DMA (Direct Memory Access): Las transferencias al display se realizan en segundo plano, permitiendo que la CPU prepare el siguiente frame mientras se env\u00eda el actual.
    • Doble Buffer con IRAM: El motor utiliza un buffer de pantalla (Sprite de TFT_eSPI) optimizado para transferencias r\u00e1pidas.
    • Alineaci\u00f3n de 16 bits: Los datos de sprites 2bpp/4bpp est\u00e1n alineados a palabras de 16 bits para aprovechar la arquitectura Xtensa.
    • IRAM_ATTR: Las funciones cr\u00edticas de renderizado est\u00e1n marcadas para residir en la RAM de instrucciones, evitando cuellos de botella por acceso a la Flash.
    "},{"location":"manual/optimization/platforms_and_drivers/#configuracion-dma","title":"Configuraci\u00f3n DMA","text":"

    El DMA se activa autom\u00e1ticamente si el hardware lo soporta. Aseg\u00farate de configurar la frecuencia SPI adecuada para tu display (usualmente 40MHz u 80MHz).

    [env:esp32dev]\nbuild_flags = \n    -D ST7789_DRIVER          # Display type\n    -D TFT_WIDTH=240          # Display width\n    -D TFT_HEIGHT=240         # Display height\n    -D TFT_MOSI=23            # SPI MOSI pin\n    -D TFT_SCLK=18            # SPI clock pin\n    -D TFT_DC=2               # Data/Command pin\n    -D TFT_RST=4              # Reset pin\n    -D TFT_CS=-1              # Chip select (-1 if not used)\n    -D SPI_FREQUENCY=40000000 # SPI frequency\n
    "},{"location":"manual/optimization/platforms_and_drivers/#supported-displays","title":"Supported Displays","text":"
    • ST7735: 128x128, 128x160
    • ST7789: 240x240, 240x320
    • ILI9341: 240x320
    • And more: See TFT_eSPI documentation
    "},{"location":"manual/optimization/platforms_and_drivers/#usage","title":"Usage","text":"
    #include <drivers/esp32/TFT_eSPI_Drawer.h>\n\n// Display configuration\npixelroot32::graphics::DisplayConfig displayConfig(\n    pixelroot32::graphics::DisplayType::ST7789,\n    0,      // rotation\n    240,    // width\n    240     // height\n);\n\n// TFT_eSPI_Drawer is created automatically by Engine\n// No manual driver creation needed\n
    "},{"location":"manual/optimization/platforms_and_drivers/#sdl2_drawer-native","title":"SDL2_Drawer (Native)","text":"

    SDL2_Drawer provides display output for PC/desktop development.

    "},{"location":"manual/optimization/platforms_and_drivers/#configuration","title":"Configuration","text":"
    #include <drivers/native/SDL2_Drawer.h>\n\n// Display configuration (NONE defaults to SDL2)\npixelroot32::graphics::DisplayConfig displayConfig(\n    pixelroot32::graphics::DisplayType::NONE,\n    0,      // rotation\n    240,    // width\n    240     // height\n);\n\n// SDL2_Drawer is created automatically\n
    "},{"location":"manual/optimization/platforms_and_drivers/#sdl2-installation","title":"SDL2 Installation","text":"

    Windows (MSYS2):

    pacman -S mingw-w64-x86_64-SDL2\n

    Linux:

    sudo apt-get install libsdl2-dev\n

    macOS:

    brew install sdl2\n

    "},{"location":"manual/optimization/platforms_and_drivers/#audio-backends","title":"Audio Backends","text":""},{"location":"manual/optimization/platforms_and_drivers/#esp32_dac_audiobackend","title":"ESP32_DAC_AudioBackend","text":"

    Uses ESP32's internal DAC for audio output.

    "},{"location":"manual/optimization/platforms_and_drivers/#configuration_1","title":"Configuration","text":"
    #include <drivers/esp32/ESP32_DAC_AudioBackend.h>\n\nconst int DAC_PIN = 25; // GPIO 25 or 26\npixelroot32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(\n    DAC_PIN,    // DAC pin (25 or 26)\n    11025       // Sample rate (Hz)\n);\n\npixelroot32::audio::AudioConfig audioConfig(\n    &audioBackend, \n    audioBackend.getSampleRate()\n);\n

    Characteristics: - Simple setup (just one pin) - Lower quality than I2S - Good for basic audio - Sample rate: 11025 Hz recommended

    "},{"location":"manual/optimization/platforms_and_drivers/#esp32_i2s_audiobackend","title":"ESP32_I2S_AudioBackend","text":"

    Uses ESP32's I2S peripheral for higher quality audio.

    "},{"location":"manual/optimization/platforms_and_drivers/#configuration_2","title":"Configuration","text":"
    #include <drivers/esp32/ESP32_I2S_AudioBackend.h>\n\nconst int I2S_BCLK = 26;  // Bit clock pin\nconst int I2S_LRCK = 25;  // Left/Right clock pin\nconst int I2S_DOUT = 22;  // Data out pin\n\npixelroot32::drivers::esp32::ESP32_I2S_AudioBackend audioBackend(\n    I2S_BCLK,\n    I2S_LRCK,\n    I2S_DOUT,\n    22050  // Sample rate (Hz)\n);\n\npixelroot32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n

    Characteristics: - Higher quality than DAC - Requires external I2S DAC (e.g., MAX98357A) - Better for music - Sample rate: 22050 Hz recommended

    "},{"location":"manual/optimization/platforms_and_drivers/#sdl2_audiobackend-native","title":"SDL2_AudioBackend (Native)","text":"

    SDL2 audio for PC development.

    "},{"location":"manual/optimization/platforms_and_drivers/#configuration_3","title":"Configuration","text":"
    #include <drivers/native/SDL2_AudioBackend.h>\n\npixelroot32::drivers::native::SDL2_AudioBackend audioBackend(\n    22050,  // Sample rate\n    1024    // Buffer size\n);\n\npixelroot32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n
    "},{"location":"manual/optimization/platforms_and_drivers/#build-flags","title":"Build Flags","text":""},{"location":"manual/optimization/platforms_and_drivers/#experimental-features","title":"Experimental Features","text":"

    Enable experimental features with build flags:

    [env:esp32dev]\nbuild_flags = \n    -D PIXELROOT32_ENABLE_2BPP_SPRITES    # Enable 2bpp sprite format\n    -D PIXELROOT32_ENABLE_4BPP_SPRITES   # Enable 4bpp sprite format\n    -D PIXELROOT32_ENABLE_SCENE_ARENA    # Enable Scene Arena (experimental)\n
    "},{"location":"manual/optimization/platforms_and_drivers/#scene-limits-max_layers-max_entities","title":"Scene limits (MAX_LAYERS / MAX_ENTITIES)","text":"

    You can override the default scene limits from your project without modifying the engine. The default of 3 for MAX_LAYERS is due to ESP32 platform constraints (memory and draw-loop cost); on native/PC you can use a higher value.

    Option A: Compiler flags (recommended) \u2014 in platformio.ini, add to build_flags for your environment:

    build_flags =\n    -DMAX_LAYERS=5\n    -DMAX_ENTITIES=64\n

    The compiler defines these before any .cpp is processed. Because Scene.h uses #ifndef MAX_LAYERS / #ifndef MAX_ENTITIES, your values are used (more render layers drawn in Scene::draw, and on Arduino the entity queue capacity when built with MAX_ENTITIES).

    See API Reference - Scene - Overriding scene limits for details.

    "},{"location":"manual/optimization/platforms_and_drivers/#platform-detection","title":"Platform Detection","text":"
    #ifdef PLATFORM_ESP32\n    // ESP32-specific code\n    Serial.println(\"Running on ESP32\");\n#endif\n\n#ifdef PLATFORM_NATIVE\n    // Native/PC-specific code\n    printf(\"Running on PC\\n\");\n#endif\n
    "},{"location":"manual/optimization/platforms_and_drivers/#optimization-flags","title":"Optimization Flags","text":"
    [env:esp32dev]\nbuild_flags = \n    -O2              # Optimization level\n    -ffunction-sections\n    -fdata-sections\n    -Wl,--gc-sections\n
    "},{"location":"manual/optimization/platforms_and_drivers/#complete-platform-setup-examples","title":"Complete Platform Setup Examples","text":""},{"location":"manual/optimization/platforms_and_drivers/#esp32-complete-setup","title":"ESP32 Complete Setup","text":"
    #include <Arduino.h>\n#include <core/Engine.h>\n#include <drivers/esp32/TFT_eSPI_Drawer.h>\n#include <drivers/esp32/ESP32_DAC_AudioBackend.h>\n\nnamespace pr32 = pixelroot32;\n\n// Audio\nconst int DAC_PIN = 25;\npr32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(DAC_PIN, 11025);\n\n// Display\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::ST7789,\n    0, 240, 240\n);\n\n// Input\npr32::input::InputConfig inputConfig(\n    6, 32, 27, 33, 14, 13, 12  // 6 buttons, pins\n);\n\n// Audio config\npr32::audio::AudioConfig audioConfig(&audioBackend, 11025);\n\n// Engine\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\nvoid setup() {\n    Serial.begin(115200);\n    engine.init();\n    // ... scene setup\n}\n\nvoid loop() {\n    engine.run();\n}\n
    "},{"location":"manual/optimization/platforms_and_drivers/#native-complete-setup","title":"Native Complete Setup","text":"
    #define SDL_MAIN_HANDLED\n#include <SDL2/SDL.h>\n#include <core/Engine.h>\n#include <drivers/native/SDL2_Drawer.h>\n#include <drivers/native/SDL2_AudioBackend.h>\n\nnamespace pr32 = pixelroot32;\n\n// Audio\npr32::drivers::native::SDL2_AudioBackend audioBackend(22050, 1024);\n\n// Display\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::NONE,\n    0, 240, 240\n);\n\n// Input (SDL scancodes)\npr32::input::InputConfig inputConfig(\n    6,\n    SDL_SCANCODE_UP,\n    SDL_SCANCODE_DOWN,\n    SDL_SCANCODE_LEFT,\n    SDL_SCANCODE_RIGHT,\n    SDL_SCANCODE_SPACE,\n    SDL_SCANCODE_RETURN\n);\n\n// Audio config\npr32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n\n// Engine\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\nint main(int argc, char* argv[]) {\n    engine.init();\n    // ... scene setup\n    engine.run();\n    return 0;\n}\n
    "},{"location":"manual/optimization/platforms_and_drivers/#platform-specific-considerations","title":"Platform-Specific Considerations","text":""},{"location":"manual/optimization/platforms_and_drivers/#esp32_1","title":"ESP32","text":"

    Memory: - Limited RAM (~320KB) - Use object pooling - Store data in flash - Avoid dynamic allocation

    Performance: - Target 30-60 FPS - Optimize rendering - Reduce entity count - Profile on hardware

    Hardware: - GPIO pin configuration - SPI display setup - Audio hardware connections - Power considerations

    "},{"location":"manual/optimization/platforms_and_drivers/#native","title":"Native","text":"

    Development: - Fast iteration - Easy debugging - Unlimited resources - Visual debugging tools

    Testing: - Test logic without hardware - Rapid prototyping - CI/CD integration - Cross-platform testing

    "},{"location":"manual/optimization/platforms_and_drivers/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/optimization/platforms_and_drivers/#esp32-display-issues","title":"ESP32 Display Issues","text":"
    • Check wiring connections
    • Verify pin numbers
    • Lower SPI frequency
    • Check display type matches
    • Verify power supply
    "},{"location":"manual/optimization/platforms_and_drivers/#esp32-audio-issues","title":"ESP32 Audio Issues","text":"
    • Check DAC/I2S pin configuration
    • Verify sample rate
    • Check hardware connections
    • Lower volume if distorted
    • Test with different sample rates
    "},{"location":"manual/optimization/platforms_and_drivers/#native-build-issues","title":"Native Build Issues","text":"
    • Verify SDL2 installation
    • Check include/library paths
    • Ensure SDL2 version compatibility
    • Check linker flags
    "},{"location":"manual/optimization/platforms_and_drivers/#next-steps","title":"Next Steps","text":"

    Now that you understand platforms and drivers, learn about: - Extensibility - Create custom drivers - Memory Management - ESP32 memory constraints - Performance Optimization - Platform-specific optimization

    See also: - API Reference - DrawSurface - API Reference - AudioBackend - Getting Started - Your First Project

    "},{"location":"reference/api_overview/","title":"API Reference Overview","text":"

    This document provides a complete technical reference for all PixelRoot32 APIs, organized by module. Each class includes descriptions, constructors, methods, properties, and usage examples.

    "},{"location":"reference/api_overview/#organization","title":"Organization","text":"

    The API is organized into the following modules:

    • Core: Engine, Scene, Entity, Actor, PhysicsActor, SceneManager
    • Graphics: Renderer, Camera2D, Color, Font, Sprite, TileMap, DrawSurface
    • Audio: AudioEngine, MusicPlayer, AudioTypes, AudioConfig, AudioBackend
    • Input: InputManager, InputConfig
    • Physics: CollisionSystem, CollisionTypes
    • UI: UIElement, UIButton, UILabel, UILayouts
    • Particles: ParticleEmitter, ParticleConfig, ParticlePresets
    "},{"location":"reference/api_overview/#quick-navigation","title":"Quick Navigation","text":""},{"location":"reference/api_overview/#core-module","title":"Core Module","text":"
    • Engine - Main engine class, game loop management
    • Scene - Scene/level management
    • Entity - Base game object class
    • Actor - Entity with collision support
    • PhysicsActor - Actor with automatic physics
    • InputManager - Input handling
    • InputConfig - Input configuration
    "},{"location":"reference/api_overview/#graphics-module","title":"Graphics Module","text":"
    • Renderer - High-level rendering API
    • Camera2D - 2D camera for scrolling
    • Color - Color constants and utilities
    • Font - Bitmap font system
    • Sprite - Sprite structures and formats
    • TileMap - Tilemap structure
    • DisplayConfig - Display configuration
    "},{"location":"reference/api_overview/#audio-module","title":"Audio Module","text":"
    • AudioEngine - Sound effects playback
    • MusicPlayer - Background music playback
    • AudioTypes - Audio data structures
    • AudioConfig - Audio configuration
    "},{"location":"reference/api_overview/#physics-module","title":"Physics Module","text":"
    • CollisionSystem - Collision detection
    • CollisionTypes - Collision primitives
    "},{"location":"reference/api_overview/#ui-module","title":"UI Module","text":"
    • UIElement - Base UI element class
    • UIButton - Clickable button
    • UILabel - Text label
    • UILayouts - Layout containers
    "},{"location":"reference/api_overview/#api-documentation-format","title":"API Documentation Format","text":"

    Each API reference page follows this structure:

    "},{"location":"reference/api_overview/#class-name","title":"Class Name","text":"

    Brief description of the class and its purpose.

    "},{"location":"reference/api_overview/#namespace","title":"Namespace","text":"
    namespace pixelroot32::module {\n    class ClassName {\n        // ...\n    };\n}\n
    "},{"location":"reference/api_overview/#constructors","title":"Constructors","text":"

    List of all constructors with parameters.

    "},{"location":"reference/api_overview/#public-methods","title":"Public Methods","text":"Method Description Parameters Returns methodName() Description param: type return type"},{"location":"reference/api_overview/#properties","title":"Properties","text":"Property Type Description property type Description"},{"location":"reference/api_overview/#usage-example","title":"Usage Example","text":"
    // Example code showing typical usage\n
    "},{"location":"reference/api_overview/#performance-notes","title":"Performance Notes","text":"

    Any performance considerations or limitations.

    "},{"location":"reference/api_overview/#see-also","title":"See Also","text":"

    Links to related APIs and documentation.

    "},{"location":"reference/api_overview/#finding-apis","title":"Finding APIs","text":""},{"location":"reference/api_overview/#by-functionality","title":"By Functionality","text":"
    • Game Loop: See Engine
    • Rendering: See Renderer
    • Input: See InputManager
    • Audio: See AudioEngine and MusicPlayer
    • Physics: See PhysicsActor and CollisionSystem
    • UI: See UIElement and layouts
    "},{"location":"reference/api_overview/#by-module","title":"By Module","text":"

    Navigate to the specific module folder: - api_reference/core/ - Core engine classes - api_reference/graphics/ - Rendering and graphics - api_reference/audio/ - Audio system - api_reference/physics/ - Physics and collisions - api_reference/ui/ - User interface

    "},{"location":"reference/api_overview/#complete-api-list","title":"Complete API List","text":""},{"location":"reference/api_overview/#core","title":"Core","text":"
    • Engine
    • Scene
    • Entity
    • Actor
    • PhysicsActor
    • InputManager
    • InputConfig
    "},{"location":"reference/api_overview/#graphics","title":"Graphics","text":"
    • Renderer
    • Camera2D
    • Color
    • Font
    • Sprite
    • TileMap
    • DisplayConfig
    "},{"location":"reference/api_overview/#audio","title":"Audio","text":"
    • AudioEngine
    • MusicPlayer
    • AudioTypes
    • AudioConfig
    "},{"location":"reference/api_overview/#physics","title":"Physics","text":"
    • CollisionSystem
    • CollisionTypes
    "},{"location":"reference/api_overview/#ui","title":"UI","text":"
    • UIElement
    • UIButton
    • UILabel
    • UIVerticalLayout
    • UIHorizontalLayout
    • UIGridLayout
    • UIAnchorLayout
    • UIPanel
    • UIPaddingContainer
    "},{"location":"reference/api_overview/#related-documentation","title":"Related Documentation","text":"
    • Manual - Game Development - How to use the APIs
    • Manual - Advanced Graphics - Advanced techniques
    • Code Examples - Reusable code snippets
    • Game Examples Guide - Learn from complete games

    Note: This is an overview. For detailed API documentation, see the individual reference pages linked above.

    "},{"location":"reference/code_examples/","title":"Code Examples","text":"

    A library of reusable code snippets for common PixelRoot32 tasks. All examples are complete and functional.

    "},{"location":"reference/code_examples/#initialization","title":"Initialization","text":""},{"location":"reference/code_examples/#basic-engine-setup-esp32","title":"Basic Engine Setup (ESP32)","text":"
    #include <Arduino.h>\n#include <core/Engine.h>\n#include <drivers/esp32/TFT_eSPI_Drawer.h>\n#include <drivers/esp32/ESP32_DAC_AudioBackend.h>\n\nnamespace pr32 = pixelroot32;\n\n// Audio\nconst int DAC_PIN = 25;\npr32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(DAC_PIN, 11025);\n\n// Display\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::ST7789,\n    0, 240, 240\n);\n\n// Input\npr32::input::InputConfig inputConfig(6, 32, 27, 33, 14, 13, 12);\n\n// Audio config\npr32::audio::AudioConfig audioConfig(&audioBackend, 11025);\n\n// Engine\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\nvoid setup() {\n    Serial.begin(115200);\n    engine.init();\n    // ... scene setup\n}\n\nvoid loop() {\n    engine.run();\n}\n
    "},{"location":"reference/code_examples/#basic-engine-setup-native","title":"Basic Engine Setup (Native)","text":"
    #define SDL_MAIN_HANDLED\n#include <SDL2/SDL.h>\n#include <core/Engine.h>\n#include <drivers/native/SDL2_Drawer.h>\n#include <drivers/native/SDL2_AudioBackend.h>\n\nnamespace pr32 = pixelroot32;\n\npr32::drivers::native::SDL2_AudioBackend audioBackend(22050, 1024);\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::NONE, 0, 240, 240\n);\npr32::input::InputConfig inputConfig(\n    6, SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, \n    SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT,\n    SDL_SCANCODE_SPACE, SDL_SCANCODE_RETURN\n);\npr32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\nint main(int argc, char* argv[]) {\n    engine.init();\n    // ... scene setup\n    engine.run();\n    return 0;\n}\n
    "},{"location":"reference/code_examples/#entity-movement","title":"Entity Movement","text":""},{"location":"reference/code_examples/#simple-movement","title":"Simple Movement","text":"
    class MovingEntity : public pixelroot32::core::Entity {\nprivate:\n    float speedX = 50.0f;\n    float speedY = 30.0f;\n\npublic:\n    MovingEntity(float x, float y)\n        : Entity(x, y, 16, 16, pixelroot32::core::EntityType::GENERIC) {\n        setRenderLayer(1);\n    }\n\n    void update(unsigned long deltaTime) override {\n        float dt = deltaTime * 0.001f;\n        x += speedX * dt;\n        y += speedY * dt;\n\n        // Bounce off edges\n        if (x < 0 || x > 224) speedX = -speedX;\n        if (y < 0 || y > 224) speedY = -speedY;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x),\n            static_cast<int>(y),\n            width, height,\n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n};\n
    "},{"location":"reference/code_examples/#input-based-movement","title":"Input-Based Movement","text":"
    class PlayerEntity : public pixelroot32::core::Actor {\nprivate:\n    float speed = 100.0f;\n\npublic:\n    PlayerEntity(float x, float y)\n        : Actor(x, y, 16, 16) {\n        setRenderLayer(1);\n    }\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        float dt = deltaTime * 0.001f;\n\n        if (input.isButtonDown(Buttons::LEFT)) {\n            x -= speed * dt;\n        }\n        if (input.isButtonDown(Buttons::RIGHT)) {\n            x += speed * dt;\n        }\n        if (input.isButtonDown(Buttons::UP)) {\n            y -= speed * dt;\n        }\n        if (input.isButtonDown(Buttons::DOWN)) {\n            y += speed * dt;\n        }\n\n        // Keep on screen\n        if (x < 0) x = 0;\n        if (x > 224) x = 224;\n        if (y < 0) y = 0;\n        if (y > 224) y = 224;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x),\n            static_cast<int>(y),\n            width, height,\n            pixelroot32::graphics::Color::White\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Handle collision\n    }\n};\n
    "},{"location":"reference/code_examples/#collisions","title":"Collisions","text":""},{"location":"reference/code_examples/#basic-collision-detection","title":"Basic Collision Detection","text":"
    class CollidableEntity : public pixelroot32::core::Actor {\npublic:\n    CollidableEntity(float x, float y)\n        : Actor(x, y, 16, 16) {\n        setRenderLayer(1);\n        setCollisionLayer(Layers::PLAYER);\n        setCollisionMask(Layers::ENEMY | Layers::WALL);\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        if (other->isInLayer(Layers::ENEMY)) {\n            // Hit enemy\n            takeDamage();\n        } else if (other->isInLayer(Layers::WALL)) {\n            // Hit wall\n            stopMovement();\n        }\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n};\n
    "},{"location":"reference/code_examples/#collision-layers-setup","title":"Collision Layers Setup","text":"
    // Define in GameLayers.h\nnamespace Layers {\n    constexpr uint16_t PLAYER = 0x0001;\n    constexpr uint16_t ENEMY = 0x0002;\n    constexpr uint16_t PROJECTILE = 0x0004;\n    constexpr uint16_t WALL = 0x0008;\n    constexpr uint16_t PICKUP = 0x0010;\n}\n\n// Usage\nplayer->setCollisionLayer(Layers::PLAYER);\nplayer->setCollisionMask(Layers::ENEMY | Layers::WALL);\n\nenemy->setCollisionLayer(Layers::ENEMY);\nenemy->setCollisionMask(Layers::PLAYER | Layers::PROJECTILE);\n
    "},{"location":"reference/code_examples/#sound-effects","title":"Sound Effects","text":""},{"location":"reference/code_examples/#common-sound-effects","title":"Common Sound Effects","text":"
    namespace SoundEffects {\n    inline pixelroot32::audio::AudioEvent jump() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::PULSE;\n        evt.frequency = 600.0f;\n        evt.duration = 0.1f;\n        evt.volume = 0.7f;\n        evt.duty = 0.25f;\n        return evt;\n    }\n\n    inline pixelroot32::audio::AudioEvent coin() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::PULSE;\n        evt.frequency = 1500.0f;\n        evt.duration = 0.12f;\n        evt.volume = 0.8f;\n        evt.duty = 0.5f;\n        return evt;\n    }\n\n    inline pixelroot32::audio::AudioEvent explosion() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::NOISE;\n        evt.frequency = 200.0f;\n        evt.duration = 0.3f;\n        evt.volume = 0.9f;\n        return evt;\n    }\n}\n\n// Usage\nengine.getAudioEngine().playEvent(SoundEffects::jump());\n
    "},{"location":"reference/code_examples/#playing-sound-on-event","title":"Playing Sound on Event","text":"
    class PlayerActor : public pixelroot32::core::Actor {\npublic:\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n\n        if (input.isButtonPressed(Buttons::A)) {\n            // Play jump sound\n            pixelroot32::audio::AudioEvent jumpSound{};\n            jumpSound.type = pixelroot32::audio::WaveType::PULSE;\n            jumpSound.frequency = 800.0f;\n            jumpSound.duration = 0.1f;\n            jumpSound.volume = 0.7f;\n            jumpSound.duty = 0.25f;\n\n            engine.getAudioEngine().playEvent(jumpSound);\n\n            // Jump logic\n            jump();\n        }\n    }\n};\n
    "},{"location":"reference/code_examples/#ui-components","title":"UI Components","text":""},{"location":"reference/code_examples/#simple-menu","title":"Simple Menu","text":"
    class MenuScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIVerticalLayout* menu;\n\npublic:\n    void init() override {\n        menu = new pixelroot32::graphics::ui::UIVerticalLayout(40, 60, 160, 160);\n        menu->setPadding(10);\n        menu->setSpacing(8);\n        menu->setNavigationButtons(0, 1);\n        menu->setButtonStyle(\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Cyan,\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Black\n        );\n\n        menu->addElement(new pixelroot32::graphics::ui::UIButton(\n            \"Start\", 0, 0, 0, 140, 25, []() { startGame(); }\n        ));\n\n        menu->addElement(new pixelroot32::graphics::ui::UIButton(\n            \"Options\", 1, 0, 0, 140, 25, []() { showOptions(); }\n        ));\n\n        addEntity(menu);\n    }\n\n    void update(unsigned long deltaTime) override {\n        menu->handleInput(engine.getInputManager());\n        Scene::update(deltaTime);\n    }\n};\n
    "},{"location":"reference/code_examples/#hud-with-labels","title":"HUD with Labels","text":"
    class GameHUD : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::ui::UILabel* scoreLabel;\n    pixelroot32::graphics::ui::UILabel* livesLabel;\n\npublic:\n    GameHUD()\n        : Entity(0, 0, 240, 240, pixelroot32::core::EntityType::UI_ELEMENT) {\n        setRenderLayer(2);\n\n        scoreLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Score: 0\", 10, 10,\n            pixelroot32::graphics::Color::White, 1\n        );\n\n        livesLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Lives: 3\", 10, 20,\n            pixelroot32::graphics::Color::White, 1\n        );\n    }\n\n    void updateHUD(int score, int lives) {\n        char buffer[32];\n        snprintf(buffer, sizeof(buffer), \"Score: %d\", score);\n        scoreLabel->setText(buffer);\n\n        snprintf(buffer, sizeof(buffer), \"Lives: %d\", lives);\n        livesLabel->setText(buffer);\n    }\n};\n
    "},{"location":"reference/code_examples/#physics","title":"Physics","text":""},{"location":"reference/code_examples/#bouncing-ball","title":"Bouncing Ball","text":"
    class BouncingBall : public pixelroot32::core::PhysicsActor {\npublic:\n    BouncingBall(float x, float y, float radius)\n        : PhysicsActor(x, y, radius * 2, radius * 2) {\n        setRenderLayer(1);\n        setRestitution(0.9f);\n        setFriction(0.05f);\n        setWorldSize(240, 240);\n        setVelocity(50.0f, -30.0f);\n    }\n\n    void update(unsigned long deltaTime) override {\n        float gravity = 200.0f;\n        float dt = deltaTime * 0.001f;\n        setVelocity(vx, vy + gravity * dt);\n        PhysicsActor::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        int radius = width / 2;\n        renderer.drawFilledCircle(\n            static_cast<int>(x + radius),\n            static_cast<int>(y + radius),\n            radius,\n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n};\n
    "},{"location":"reference/code_examples/#platformer-player","title":"Platformer Player","text":"
    class PlatformerPlayer : public pixelroot32::core::PhysicsActor {\nprivate:\n    bool canJump = true;\n    float jumpForce = 250.0f;\n    float moveSpeed = 100.0f;\n\npublic:\n    PlatformerPlayer(float x, float y)\n        : PhysicsActor(x, y, 16, 16) {\n        setRenderLayer(1);\n        setFriction(0.3f);\n        setWorldSize(240, 240);\n    }\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        float dt = deltaTime * 0.001f;\n\n        // Horizontal movement\n        float moveDir = 0.0f;\n        if (input.isButtonDown(Buttons::LEFT)) moveDir -= 1.0f;\n        if (input.isButtonDown(Buttons::RIGHT)) moveDir += 1.0f;\n        setVelocity(moveDir * moveSpeed, vy);\n\n        // Gravity\n        float gravity = 300.0f;\n        setVelocity(vx, vy + gravity * dt);\n\n        // Jump\n        if (input.isButtonPressed(Buttons::A) && canJump) {\n            setVelocity(vx, -jumpForce);\n            canJump = false;\n        }\n\n        PhysicsActor::update(deltaTime);\n\n        // Check if on ground\n        auto collisionInfo = getWorldCollisionInfo();\n        if (collisionInfo.bottom) {\n            canJump = true;\n        }\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n};\n
    "},{"location":"reference/code_examples/#sprites-and-animation","title":"Sprites and Animation","text":""},{"location":"reference/code_examples/#simple-sprite","title":"Simple Sprite","text":"
    // Define sprite data\nstatic const uint16_t PLAYER_SPRITE_DATA[] = {\n    0b00111100,\n    0b01111110,\n    0b11111111,\n    0b11111111,\n    0b11111111,\n    0b01111110,\n    0b00111100,\n    0b00000000\n};\n\nstatic const pixelroot32::graphics::Sprite PLAYER_SPRITE = {\n    PLAYER_SPRITE_DATA, 8, 8\n};\n\n// Draw sprite\nrenderer.drawSprite(PLAYER_SPRITE, 100, 100, \n    pixelroot32::graphics::Color::White);\n
    "},{"location":"reference/code_examples/#sprite-animation","title":"Sprite Animation","text":"
    class AnimatedActor : public pixelroot32::core::Actor {\nprivate:\n    pixelroot32::graphics::SpriteAnimation animation;\n    unsigned long timer = 0;\n    const unsigned long FRAME_DURATION_MS = 100;\n\npublic:\n    AnimatedActor(float x, float y)\n        : Actor(x, y, 8, 8) {\n        setRenderLayer(1);\n        animation.frames = WALK_ANIMATION_FRAMES;\n        animation.frameCount = 3;\n        animation.current = 0;\n    }\n\n    void update(unsigned long deltaTime) override {\n        timer += deltaTime;\n        if (timer >= FRAME_DURATION_MS) {\n            timer -= FRAME_DURATION_MS;\n            animation.step();\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        const auto* frame = animation.frames[animation.current].sprite;\n        renderer.drawSprite(*frame, static_cast<int>(x), static_cast<int>(y),\n            pixelroot32::graphics::Color::White);\n    }\n};\n
    "},{"location":"reference/code_examples/#camera-and-scrolling","title":"Camera and Scrolling","text":""},{"location":"reference/code_examples/#basic-camera-follow","title":"Basic Camera Follow","text":"
    class ScrollingScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    PlayerActor* player;\n\npublic:\n    void init() override {\n        int screenWidth = engine.getRenderer().getWidth();\n        int screenHeight = engine.getRenderer().getHeight();\n\n        camera = pixelroot32::graphics::Camera2D(screenWidth, screenHeight);\n        camera.setBounds(0, 2000 - screenWidth);\n\n        player = new PlayerActor(100, 100);\n        addEntity(player);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n        camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        camera.apply(renderer);\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"reference/code_examples/#tilemaps","title":"Tilemaps","text":""},{"location":"reference/code_examples/#simple-tilemap","title":"Simple Tilemap","text":"
    // Define tiles\nstatic const uint16_t TILE_EMPTY_BITS[] = { /* ... */ };\nstatic const uint16_t TILE_GROUND_BITS[] = { /* ... */ };\n\nstatic const pixelroot32::graphics::Sprite TILES[] = {\n    { TILE_EMPTY_BITS, 8, 8 },\n    { TILE_GROUND_BITS, 8, 8 }\n};\n\n// Create tilemap\nstatic uint8_t TILEMAP_INDICES[30 * 20];\nstatic pixelroot32::graphics::TileMap levelTileMap = {\n    TILEMAP_INDICES, 30, 20, TILES, 8, 8, 2\n};\n\n// Initialize\nvoid initTilemap() {\n    for (int i = 0; i < 30 * 20; i++) {\n        TILEMAP_INDICES[i] = 0;\n    }\n    // Set ground row\n    for (int x = 0; x < 30; x++) {\n        TILEMAP_INDICES[19 * 30 + x] = 1; // Ground tile\n    }\n}\n\n// Draw\nrenderer.drawTileMap(levelTileMap, 0, 0, \n    pixelroot32::graphics::Color::White);\n
    "},{"location":"reference/code_examples/#particles","title":"Particles","text":""},{"location":"reference/code_examples/#explosion-effect","title":"Explosion Effect","text":"
    #include <graphics/particles/ParticleEmitter.h>\n#include <graphics/particles/ParticlePresets.h>\n\nclass ExplosionEffect : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::particles::ParticleEmitter* emitter;\n\npublic:\n    ExplosionEffect()\n        : Entity(0, 0, 1, 1, pixelroot32::core::EntityType::GENERIC) {\n        setRenderLayer(1);\n        emitter = new pixelroot32::graphics::particles::ParticleEmitter(\n            0, 0,\n            pixelroot32::graphics::particles::ParticlePresets::Explosion()\n        );\n    }\n\n    void trigger(float x, float y) {\n        this->x = x;\n        this->y = y;\n        emitter->burst(x, y, 25);\n    }\n\n    void update(unsigned long deltaTime) override {\n        emitter->update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        emitter->draw(renderer);\n    }\n};\n
    "},{"location":"reference/code_examples/#object-pooling","title":"Object Pooling","text":""},{"location":"reference/code_examples/#entity-pool","title":"Entity Pool","text":"
    template<typename T, int POOL_SIZE>\nclass EntityPool {\nprivate:\n    T pool[POOL_SIZE];\n    bool inUse[POOL_SIZE];\n    int activeCount = 0;\n\npublic:\n    EntityPool() {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            inUse[i] = false;\n        }\n    }\n\n    T* acquire() {\n        if (activeCount >= POOL_SIZE) return nullptr;\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (!inUse[i]) {\n                inUse[i] = true;\n                activeCount++;\n                return &pool[i];\n            }\n        }\n        return nullptr;\n    }\n\n    void release(T* obj) {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (&pool[i] == obj) {\n                inUse[i] = false;\n                activeCount--;\n                obj->isEnabled = false;\n                obj->isVisible = false;\n                break;\n            }\n        }\n    }\n};\n
    "},{"location":"reference/code_examples/#common-patterns","title":"Common Patterns","text":""},{"location":"reference/code_examples/#state-machine","title":"State Machine","text":"
    enum class GameState {\n    MENU,\n    PLAYING,\n    PAUSED,\n    GAME_OVER\n};\n\nclass GameScene : public pixelroot32::core::Scene {\nprivate:\n    GameState currentState = GameState::MENU;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        switch (currentState) {\n            case GameState::MENU:\n                updateMenu();\n                break;\n            case GameState::PLAYING:\n                updateGame();\n                break;\n            case GameState::PAUSED:\n                updatePause();\n                break;\n            case GameState::GAME_OVER:\n                updateGameOver();\n                break;\n        }\n        Scene::update(deltaTime);\n    }\n};\n
    "},{"location":"reference/code_examples/#timer-pattern","title":"Timer Pattern","text":"
    class Timer {\nprivate:\n    unsigned long duration;\n    unsigned long elapsed = 0;\n    bool active = false;\n\npublic:\n    Timer(unsigned long ms) : duration(ms) {}\n\n    void start() {\n        active = true;\n        elapsed = 0;\n    }\n\n    void update(unsigned long deltaTime) {\n        if (active) {\n            elapsed += deltaTime;\n            if (elapsed >= duration) {\n                active = false;\n            }\n        }\n    }\n\n    bool isFinished() const { return !active && elapsed >= duration; }\n    bool isActive() const { return active; }\n    float getProgress() const { return static_cast<float>(elapsed) / duration; }\n};\n
    "},{"location":"reference/code_examples/#see-also","title":"See Also","text":"
    • API Reference Overview - Complete API documentation
    • Game Examples Guide - Learn from complete games
    • Manual - Game Development - Detailed guides
    "},{"location":"reference/game_examples_guide/","title":"Game Examples Guide","text":"

    This guide analyzes the complete game examples included with PixelRoot32, explaining their architecture, patterns, and lessons learned.

    "},{"location":"reference/game_examples_guide/#available-examples","title":"Available Examples","text":"

    PixelRoot32 (en el proyecto PixelRoot32 Game Samples) incluye estos juegos y demos:

    • Metroidvania: Plataformas 2D con tilemap 4bpp multicapa y colisi\u00f3n tile-based (requiere PIXELROOT32_ENABLE_4BPP_SPRITES; sin scroll/c\u00e1mara)
    • Space Invaders: Shooter completo con enemigos, proyectiles, b\u00fankeres y audio
    • Pong: Cl\u00e1sico con f\u00edsica y colisiones
    • BrickBreaker: Estilo Breakout con part\u00edculas y audio avanzado
    • Snake: Juego en rejilla con entity pooling
    • TicTacToe: Turnos y IA simple
    • CameraDemo: Plataformas con c\u00e1mara y parallax
    • SpritesDemo: Sprites 2bpp y 4bpp
    • TileMapDemo: Tilemaps 4bpp (con viewport culling)
    "},{"location":"reference/game_examples_guide/#space-invaders","title":"Space Invaders","text":"

    Location: src/examples/SpaceInvaders/

    "},{"location":"reference/game_examples_guide/#architecture","title":"Architecture","text":"

    Space Invaders demonstrates a complete game with multiple systems:

    • Scene Management: SpaceInvadersScene manages game state
    • Actor Hierarchy: PlayerActor, AlienActor, ProjectileActor, BunkerActor
    • Collision System: Uses collision layers for player, enemies, projectiles
    • Audio Integration: Sound effects for shooting, explosions, music
    • Background: Starfield (patr\u00f3n de estrellas en c\u00f3digo) o tilemap
    "},{"location":"reference/game_examples_guide/#key-systems","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#collision-layers","title":"Collision Layers","text":"
    namespace Layers {\n    constexpr uint16_t PLAYER = 0x0001;\n    constexpr uint16_t ALIEN = 0x0002;\n    constexpr uint16_t PROJECTILE = 0x0004;\n    constexpr uint16_t BUNKER = 0x0008;\n}\n\n// Player can collide with aliens and bunkers\nplayer->setCollisionLayer(Layers::PLAYER);\nplayer->setCollisionMask(Layers::ALIEN | Layers::BUNKER);\n\n// Projectiles can hit aliens and bunkers\nprojectile->setCollisionLayer(Layers::PROJECTILE);\nprojectile->setCollisionMask(Layers::ALIEN | Layers::BUNKER);\n
    "},{"location":"reference/game_examples_guide/#entity-management","title":"Entity Management","text":"
    • Uses object pooling for projectiles
    • Manages alien formation with grid layout
    • Handles game state (playing, game over)
    "},{"location":"reference/game_examples_guide/#audio-integration","title":"Audio Integration","text":"
    • Background music using MusicPlayer
    • Sound effects for various events
    • Audio events triggered on collisions
    "},{"location":"reference/game_examples_guide/#patterns-used","title":"Patterns Used","text":"
    • Object Pooling: Projectiles are pooled and reused
    • State Machine: Game states (playing, game over, victory)
    • Grid Layout: Alien formation uses grid-based positioning
    • Event-Driven Audio: Sounds triggered by game events
    "},{"location":"reference/game_examples_guide/#lessons-learned","title":"Lessons Learned","text":"
    • Collision layers are essential for complex games
    • Object pooling improves performance
    • Starfield or tilemap backgrounds are efficient
    • Audio enhances game feel significantly
    "},{"location":"reference/game_examples_guide/#metroidvania","title":"Metroidvania","text":"

    Ubicaci\u00f3n: src/examples/Games/Metroidvania/

    "},{"location":"reference/game_examples_guide/#arquitectura","title":"Arquitectura","text":"

    Metroidvania es un ejemplo de plataformas 2D con tilemap multicapa y optimizaciones pensadas para ESP32. No usa scroll ni c\u00e1mara; el nivel se dibuja con origen fijo (0,0).

    • Scene: MetroidvaniaScene con un \u00fanico PlayerActor y varias capas de tilemap (background, platforms, details, stairs).
    • PlayerActor: Movimiento horizontal y vertical, escaleras, colisi\u00f3n tile-based (sin listas de rect\u00e1ngulos).
    • Tilemap: 4bpp (TileMap4bpp), con viewport culling y cach\u00e9 de paleta en el motor. Nivel 40\u00d730 tiles (320\u00d7240 px).
    • Sin c\u00e1mara: La vista no sigue al jugador; para scroll habr\u00eda que usar Camera2D y aplicar offset en el renderer (como en CameraDemo).
    "},{"location":"reference/game_examples_guide/#caracteristicas-del-motor-usadas","title":"Caracter\u00edsticas del motor usadas","text":"
    • Colisi\u00f3n tile-based: Comprobaci\u00f3n directa de tiles alrededor del jugador (getTileAt), en lugar de iterar sobre platformRects.
    • Sprites 4bpp: Player con animaciones (idle, run, jump) desde headers generados (p. ej. Sprite Compiler).
    • Optimizaciones de renderizado: Viewport culling en drawTileMap, drawSprite 4bpp optimizado, capas culleadas por viewport.
    • Opcional: Scene arena, DMA, IRAM_ATTR en rutas cr\u00edticas (seg\u00fan plan de optimizaci\u00f3n del ejemplo).
    "},{"location":"reference/game_examples_guide/#patrones-utilizados","title":"Patrones utilizados","text":"
    • Tile-based collision: Un solo acceso por tile en O(1) en lugar de O(N) rect\u00e1ngulos.
    • Detecci\u00f3n de escaleras: Un solo resultado reutilizado para colisi\u00f3n y cambio de estado.
    • Hitbox simplificada: Menos puntos de comprobaci\u00f3n vertical (cabeza y pies).
    "},{"location":"reference/game_examples_guide/#lecciones","title":"Lecciones","text":"
    • La colisi\u00f3n tile-based escala mejor que listas de rect\u00e1ngulos en niveles grandes.
    • Las optimizaciones de viewport y 4bpp mejoran FPS en ESP32.
    • Metroidvania sirve de referencia para juegos de plataformas con tilemap y c\u00e1mara.
    "},{"location":"reference/game_examples_guide/#pong","title":"Pong","text":"

    Location: src/examples/Pong/

    "},{"location":"reference/game_examples_guide/#architecture_1","title":"Architecture","text":"

    Pong demonstrates physics and collision handling:

    • PhysicsActor: Ball uses PhysicsActor for automatic physics
    • Collision Callbacks: Paddles and ball handle collisions
    • Score System: Simple score tracking and display
    • Game State: Reset and game over handling
    "},{"location":"reference/game_examples_guide/#key-systems_1","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#physics-setup","title":"Physics Setup","text":"
    class BallActor : public pixelroot32::core::PhysicsActor {\npublic:\n    BallActor(float x, float y, float speed, int radius)\n        : PhysicsActor(x, y, radius * 2, radius * 2) {\n        setRestitution(0.8f);  // Bouncy\n        setFriction(0.1f);     // Low friction\n        setWorldSize(240, 240);\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#collision-response","title":"Collision Response","text":"
    void BallActor::onCollision(pixelroot32::core::Actor* other) {\n    // Adjust ball position\n    // Modify velocity based on impact point\n    // Play bounce sound\n}\n
    "},{"location":"reference/game_examples_guide/#patterns-used_1","title":"Patterns Used","text":"
    • Physics Integration: Uses PhysicsActor for automatic movement
    • Collision Response: Custom collision handling
    • Score Management: Simple state tracking
    • Audio Feedback: Sound on collision
    "},{"location":"reference/game_examples_guide/#lessons-learned_1","title":"Lessons Learned","text":"
    • PhysicsActor simplifies physics-based games
    • Collision callbacks allow custom response logic
    • Simple games can be very effective
    "},{"location":"reference/game_examples_guide/#snake","title":"Snake","text":"

    Location: src/examples/Snake/

    "},{"location":"reference/game_examples_guide/#architecture_2","title":"Architecture","text":"

    Snake demonstrates entity pooling and grid-based movement:

    • Entity Pooling: Snake segments are pooled
    • Grid Movement: Movement constrained to grid
    • Game Logic: Food spawning, collision detection
    • State Management: Game over, reset functionality
    "},{"location":"reference/game_examples_guide/#key-systems_2","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#entity-pooling","title":"Entity Pooling","text":"
    class SnakeScene {\nprivate:\n    std::vector<SnakeSegmentActor*> segmentPool;\n    std::vector<SnakeSegmentActor*> snakeSegments;\n\n    void resetGame() {\n        // Reuse pooled segments\n        for (int i = 0; i < initialLength; ++i) {\n            SnakeSegmentActor* segment = segmentPool[i];\n            segment->resetAlive();\n            snakeSegments.push_back(segment);\n            addEntity(segment);\n        }\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#grid-based-movement","title":"Grid-Based Movement","text":"
    class SnakeSegmentActor : public pixelroot32::core::Actor {\nprivate:\n    int cellX, cellY;  // Grid position\n\npublic:\n    void setCellPosition(int x, int y) {\n        cellX = x;\n        cellY = y;\n        // Convert to world position\n        this->x = cellX * CELL_SIZE;\n        this->y = cellY * CELL_SIZE;\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#patterns-used_2","title":"Patterns Used","text":"
    • Object Pooling: Segments are pre-allocated and reused
    • Grid System: Discrete grid-based movement
    • Linked List: Snake segments form a linked structure
    • Food Spawning: Random food placement with collision checking
    "},{"location":"reference/game_examples_guide/#lessons-learned_2","title":"Lessons Learned","text":"
    • Entity pooling is essential for dynamic entities
    • Grid-based movement simplifies collision detection
    • Pre-allocation avoids memory fragmentation
    "},{"location":"reference/game_examples_guide/#tictactoe","title":"TicTacToe","text":"

    Location: src/examples/TicTacToe/

    "},{"location":"reference/game_examples_guide/#architecture_3","title":"Architecture","text":"

    TicTacToe demonstrates turn-based logic and simple AI:

    • Turn Management: Player vs AI turns
    • Game Board: 3x3 grid representation
    • Win Detection: Check for winning conditions
    • Simple AI: Random move selection
    "},{"location":"reference/game_examples_guide/#key-systems_3","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#board-representation","title":"Board Representation","text":"
    class TicTacToeScene {\nprivate:\n    int board[3][3];  // 0=empty, 1=X, 2=O\n    bool playerTurn = true;\n\n    bool makeMove(int row, int col, int player) {\n        if (board[row][col] == 0) {\n            board[row][col] = player;\n            return true;\n        }\n        return false;\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#win-detection","title":"Win Detection","text":"
    int checkWinner() {\n    // Check rows\n    for (int i = 0; i < 3; i++) {\n        if (board[i][0] == board[i][1] && board[i][1] == board[i][2]) {\n            return board[i][0];\n        }\n    }\n    // Check columns, diagonals...\n    return 0; // No winner\n}\n
    "},{"location":"reference/game_examples_guide/#patterns-used_3","title":"Patterns Used","text":"
    • State Machine: Turn-based state management
    • Grid Logic: 2D array for board representation
    • Simple AI: Random valid move selection
    • UI Integration: Buttons for player input
    "},{"location":"reference/game_examples_guide/#lessons-learned_3","title":"Lessons Learned","text":"
    • Turn-based games are straightforward to implement
    • Simple AI can be effective for basic games
    • Grid-based logic is easy to reason about
    "},{"location":"reference/game_examples_guide/#camerademo","title":"CameraDemo","text":"

    Location: src/examples/CameraDemo/

    "},{"location":"reference/game_examples_guide/#architecture_4","title":"Architecture","text":"

    CameraDemo demonstrates scrolling and parallax:

    • Camera2D: Camera following player
    • Tilemap: Level built with tilemap
    • Parallax: Multiple background layers
    • Platformer Physics: Player with jumping and gravity
    "},{"location":"reference/game_examples_guide/#key-systems_4","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#camera-setup","title":"Camera Setup","text":"
    class CameraDemoScene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    float levelWidth;\n\npublic:\n    void init() override {\n        camera = pixelroot32::graphics::Camera2D(240, 240);\n        camera.setBounds(0, levelWidth - 240);\n    }\n\n    void update(unsigned long deltaTime) override {\n        camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        camera.apply(renderer);\n        renderer.drawTileMap(levelTileMap, 0, 0, Color::White);\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#platformer-physics","title":"Platformer Physics","text":"
    class PlayerCube : public pixelroot32::core::PhysicsActor {\npublic:\n    void update(unsigned long deltaTime) override {\n        // Input handling\n        // Gravity application\n        // Jump logic\n        // Platform collision\n        PhysicsActor::update(deltaTime);\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#patterns-used_4","title":"Patterns Used","text":"
    • Camera Following: Dead-zone camera following
    • Tilemap Rendering: Efficient level rendering
    • Parallax Scrolling: Multiple background layers
    • Platform Collision: Custom collision with platforms
    "},{"location":"reference/game_examples_guide/#lessons-learned_4","title":"Lessons Learned","text":"
    • Camera system enables large levels
    • Tilemaps are efficient for level data
    • Parallax adds depth to 2D games
    • Platform collision requires custom logic
    "},{"location":"reference/game_examples_guide/#spritesdemo","title":"SpritesDemo","text":"

    Location: src/examples/SpritesDemo/

    "},{"location":"reference/game_examples_guide/#architecture_5","title":"Architecture","text":"

    SpritesDemo showcases advanced sprite formats:

    • 2bpp Sprites: 4-color sprite format
    • 4bpp Sprites: 16-color sprite format (if enabled)
    • Animation: Sprite animation examples
    • Format Comparison: Side-by-side format display
    "},{"location":"reference/game_examples_guide/#key-systems_5","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#2bpp-sprite-usage","title":"2bpp Sprite Usage","text":"
    #ifdef PIXELROOT32_ENABLE_2BPP_SPRITES\nstatic const pixelroot32::graphics::Sprite2bpp SPRITE_2BPP = {\n    SPRITE_DATA,\n    SPRITE_PALETTE,\n    16, 32, 4\n};\n\nrenderer.drawSprite(SPRITE_2BPP, x, y, false);\n#endif\n
    "},{"location":"reference/game_examples_guide/#animation-display","title":"Animation Display","text":"
    class SpritesDemoActor : public pixelroot32::core::Entity {\nprivate:\n    unsigned long timer = 0;\n    uint8_t currentFrame = 0;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        timer += deltaTime;\n        if (timer >= 150) {\n            timer -= 150;\n            currentFrame = (currentFrame + 1) % 9;\n        }\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#patterns-used_5","title":"Patterns Used","text":"
    • Format Comparison: Shows different sprite formats
    • Animation Loop: Frame-based animation
    • Conditional Compilation: Uses build flags
    "},{"location":"reference/game_examples_guide/#lessons-learned_5","title":"Lessons Learned","text":"
    • Advanced formats provide more color options
    • Animation is straightforward with frame arrays
    • Build flags enable/disable experimental features
    "},{"location":"reference/game_examples_guide/#common-patterns-across-examples","title":"Common Patterns Across Examples","text":""},{"location":"reference/game_examples_guide/#scene-initialization","title":"Scene Initialization","text":"

    All examples follow this pattern:

    void init() override {\n    // 1. Set palette\n    pixelroot32::graphics::setPalette(PaletteType::NES);\n\n    // 2. Create background entity\n    addEntity(new BackgroundEntity());\n\n    // 3. Create game entities\n    player = new PlayerActor(...);\n    addEntity(player);\n\n    // 4. Initialize game state\n    resetGame();\n}\n
    "},{"location":"reference/game_examples_guide/#update-pattern","title":"Update Pattern","text":"
    void update(unsigned long deltaTime) override {\n    // 1. Process input\n    handleInput();\n\n    // 2. Update game logic\n    updateGameLogic();\n\n    // 3. Call parent update (updates all entities)\n    Scene::update(deltaTime);\n\n    // 4. Post-update logic\n    checkGameState();\n}\n
    "},{"location":"reference/game_examples_guide/#draw-pattern","title":"Draw Pattern","text":"
    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // 1. Apply camera (if used)\n    camera.apply(renderer);\n\n    // 2. Draw background/tilemap\n    renderer.drawTileMap(background, 0, 0, Color::White);\n\n    // 3. Call parent draw (draws all entities)\n    Scene::draw(renderer);\n\n    // 4. Draw UI/HUD\n    drawHUD(renderer);\n}\n
    "},{"location":"reference/game_examples_guide/#learning-path","title":"Learning Path","text":""},{"location":"reference/game_examples_guide/#beginner-examples","title":"Beginner Examples","text":"
    1. Pong: F\u00edsica y colisiones b\u00e1sicas
    2. TicTacToe: L\u00f3gica por turnos
    3. Snake: Entity pooling y rejilla
    "},{"location":"reference/game_examples_guide/#intermediate-examples","title":"Intermediate Examples","text":"
    1. CameraDemo: C\u00e1mara y parallax
    2. SpritesDemo: Formatos 2bpp y 4bpp
    3. BrickBreaker: F\u00edsica, part\u00edculas y audio
    "},{"location":"reference/game_examples_guide/#advanced-examples","title":"Advanced Examples","text":"
    1. Space Invaders: Juego completo (sprites 1bpp, colisiones, audio)
    2. Metroidvania: Plataformas con tilemap 4bpp multicapa, colisi\u00f3n tile-based y optimizaciones ESP32 (sin scroll/c\u00e1mara)
    "},{"location":"reference/game_examples_guide/#code-study-recommendations","title":"Code Study Recommendations","text":""},{"location":"reference/game_examples_guide/#for-learning-physics","title":"For Learning Physics","text":"
    • Study Pong/BallActor.cpp - PhysicsActor usage
    • Study CameraDemo/PlayerCube.cpp - Platformer physics
    "},{"location":"reference/game_examples_guide/#for-learning-collisions","title":"For Learning Collisions","text":"
    • Study SpaceInvaders - Complex collision layers
    • Study Pong - Simple collision response
    "},{"location":"reference/game_examples_guide/#for-learning-memory-management","title":"For Learning Memory Management","text":"
    • Study Snake/SnakeScene.cpp - Entity pooling
    • Study SpaceInvaders - Projectile pooling
    "},{"location":"reference/game_examples_guide/#for-learning-audio","title":"For Learning Audio","text":"
    • Study SpaceInvaders - Music and sound effects
    • Study Pong - Simple audio integration
    "},{"location":"reference/game_examples_guide/#for-learning-ui","title":"For Learning UI","text":"
    • Study TicTacToe - Button-based UI
    • Study menu scenes - Layout usage
    "},{"location":"reference/game_examples_guide/#extending-examples","title":"Extending Examples","text":""},{"location":"reference/game_examples_guide/#adding-features","title":"Adding Features","text":"
    • Pong: Add power-ups, multiple balls
    • Snake: Add obstacles, multiple food types
    • Space Invaders: Add boss battles, power-ups
    "},{"location":"reference/game_examples_guide/#creating-variations","title":"Creating Variations","text":"
    • Pong: Make it vertical, add walls
    • Snake: Change to hexagonal grid
    • TicTacToe: Make it 4x4 or 5x5
    "},{"location":"reference/game_examples_guide/#best-practices-from-examples","title":"Best Practices from Examples","text":"
    1. Pre-allocate Resources: All examples pre-allocate entities
    2. Use Object Pooling: For frequently created/destroyed entities
    3. Organize by Layers: Clear collision layer organization
    4. Separate Concerns: Game logic separate from rendering
    5. State Management: Clear game state handling
    "},{"location":"reference/game_examples_guide/#see-also","title":"See Also","text":"
    • Code Examples - Reusable code snippets
    • API Reference Overview - Complete API documentation
    • Manual - Game Development - Detailed guides

    Note: All example code is available in the src/examples/ directory of the PixelRoot32 Game Samples project.

    "},{"location":"resources/available_tools/","title":"Available Tools","text":"

    This guide documents tools available to facilitate PixelRoot32 game development.

    "},{"location":"resources/available_tools/#sprite-compiler-pr32-sprite-compiler","title":"Sprite Compiler (pr32-sprite-compiler)","text":"

    The Sprite Compiler converts PNG images to PixelRoot32 sprite data formats, making it easy to create sprites from image files.

    Read more in the Sprite Compiler Guide

    From Source:

    git clone https://github.com/Gperez88/pr32-sprite-compiler.git\ncd pr32-sprite-compiler\nnpm install\nnpm link  # Optional: install globally\n

    As NPM Package:

    npm install -g pr32-sprite-compiler\n
    "},{"location":"resources/available_tools/#basic-usage","title":"Basic Usage","text":"

    Command Line:

    pr32-sprite-compiler input.png output.h\n

    With Options:

    pr32-sprite-compiler input.png output.h --format 1bpp --name MY_SPRITE\n
    "},{"location":"resources/available_tools/#supported-formats","title":"Supported Formats","text":"
    • 1bpp (default): Monochrome, most memory-efficient
    • 2bpp: 4 colors per sprite (requires PIXELROOT32_ENABLE_2BPP_SPRITES)
    • 4bpp: 16 colors per sprite (requires PIXELROOT32_ENABLE_4BPP_SPRITES)
    "},{"location":"resources/available_tools/#output-format","title":"Output Format","text":"

    The compiler generates C++ header files with sprite data:

    // output.h\n#ifndef SPRITE_DATA_H\n#define SPRITE_DATA_H\n\n#include <stdint.h>\n\nstatic const uint16_t MY_SPRITE_DATA[] = {\n    0b00111100,\n    0b01111110,\n    0b11111111,\n    // ... more rows\n};\n\nstatic const pixelroot32::graphics::Sprite MY_SPRITE = {\n    MY_SPRITE_DATA,\n    8,  // width\n    8   // height\n};\n\n#endif\n
    "},{"location":"resources/available_tools/#advanced-options","title":"Advanced Options","text":"

    Batch Processing:

    pr32-sprite-compiler --batch sprites/*.png --output-dir sprites/out/\n

    Custom Palette:

    pr32-sprite-compiler input.png output.h --palette custom_palette.json\n

    Sprite Sheet:

    pr32-sprite-compiler sheet.png output.h --sheet 8x8 --count 16\n
    "},{"location":"resources/available_tools/#gui-version","title":"GUI Version","text":"

    If available, a GUI version provides visual feedback:

    • Drag and drop images
    • Preview sprite data
    • Adjust settings visually
    • Export to header files
    "},{"location":"resources/available_tools/#step-by-step-example","title":"Step-by-Step Example","text":"
    1. Create or find a PNG image (8x8, 16x16, etc.)

    2. Run the compiler:

    pr32-sprite-compiler player.png player_sprite.h --name PLAYER_SPRITE\n
    1. Include in your project:
    #include \"player_sprite.h\"\n\nvoid draw() {\n    renderer.drawSprite(PLAYER_SPRITE, 100, 100, Color::White);\n}\n
    "},{"location":"resources/available_tools/#troubleshooting","title":"Troubleshooting","text":"

    Image too large:

    • Sprites must be \u2264 16 pixels wide for 1bpp
    • Reduce image size or split into multiple sprites

    Colors not converting correctly:

    • Ensure image uses indexed colors
    • Use black/white for 1bpp
    • Use 4 colors for 2bpp, 16 for 4bpp

    Output file not found:

    • Check write permissions
    • Verify output path exists
    "},{"location":"resources/available_tools/#future-tools","title":"Future Tools","text":""},{"location":"resources/available_tools/#music-compiler-planned","title":"Music Compiler (Planned)","text":"

    A tool to convert music files or MIDI to PixelRoot32 MusicTrack format.

    Planned Features:

    • MIDI to MusicTrack conversion
    • Visual music editor
    • Instrument preset management
    • Export to C++ header files
    "},{"location":"resources/available_tools/#tilemap-compiler-planned","title":"Tilemap Compiler (Planned)","text":"

    A tool to create tilemaps from image files or tile editors.

    Planned Features:

    • Image to tilemap conversion
    • Tile editor integration
    • Export to C++ arrays
    • Collision data generation
    "},{"location":"resources/available_tools/#other-planned-tools","title":"Other Planned Tools","text":"
    • Save System Generator: Generate save/load code
    • Asset Packer: Bundle assets for distribution
    • Performance Profiler: Analyze game performance
    "},{"location":"resources/available_tools/#using-tools-in-development","title":"Using Tools in Development","text":""},{"location":"resources/available_tools/#workflow-integration","title":"Workflow Integration","text":"

    Typical Workflow:

    1. Create/edit sprites in image editor
    2. Compile sprites to C++ headers
    3. Include headers in project
    4. Use sprites in code

    Automation:

    # Build script example\n#!/bin/bash\npr32-sprite-compiler assets/sprites/*.png --output-dir src/sprites/\n# Continue with build...\n
    "},{"location":"resources/available_tools/#best-practices","title":"Best Practices","text":"
    • Organize assets: Keep source images separate from generated code
    • Version control: Commit generated headers, not source images (or both)
    • Naming conventions: Use consistent naming for sprites
    • Batch processing: Process multiple sprites at once when possible
    "},{"location":"resources/available_tools/#see-also","title":"See Also","text":"
    • Sprite Compiler Documentation - Detailed sprite compiler guide
    • Manual - Sprites and Animation - Using sprites in games
    • Troubleshooting - Common tool issues

    Note: Tool availability may vary. Check the PixelRoot32 repository for the latest tool information.

    "},{"location":"resources/faq/","title":"Frequently Asked Questions","text":"

    Common questions about PixelRoot32, organized by category.

    "},{"location":"resources/faq/#general","title":"General","text":""},{"location":"resources/faq/#what-is-pixelroot32","title":"What is PixelRoot32?","text":"

    PixelRoot32 is a lightweight, modular 2D game engine designed for ESP32 microcontrollers. It provides a complete game development framework with rendering, audio, physics, input, and UI systems, optimized for limited hardware resources.

    "},{"location":"resources/faq/#what-platforms-does-it-support","title":"What platforms does it support?","text":"
    • ESP32: Primary platform (TFT displays, GPIO buttons, DAC/I2S audio)
    • Native/Desktop: Development platform (SDL2, keyboard, SDL2 audio)
    "},{"location":"resources/faq/#what-kind-of-games-can-i-make","title":"What kind of games can I make?","text":"

    PixelRoot32 is ideal for: - Retro/arcade-style games - 2D platformers - Shooters - Puzzle games - Simple RPGs - Educational games

    See Limitations and Considerations for what's not suitable.

    "},{"location":"resources/faq/#is-it-free-to-use","title":"Is it free to use?","text":"

    Yes, PixelRoot32 is open source and licensed under the MIT License. You can use it freely for personal and commercial projects.

    "},{"location":"resources/faq/#where-can-i-find-the-source-code","title":"Where can I find the source code?","text":"
    • Engine: https://github.com/Gperez88/PixelRoot32-Game-Engine
    • Samples: https://github.com/Gperez88/PixelRoot32-Game-Samples
    • Documentation: https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Docs
    "},{"location":"resources/faq/#installation-and-configuration","title":"Installation and Configuration","text":""},{"location":"resources/faq/#how-do-i-install-pixelroot32","title":"How do I install PixelRoot32?","text":"

    See Your First Project for detailed installation instructions.

    Quick answer: 1. Install PlatformIO in VS Code 2. Create new ESP32 project 3. Add library dependency: gperez88/PixelRoot32-Game-Engine@0.2.0-dev 4. Configure hardware in platformio.ini

    "},{"location":"resources/faq/#what-version-should-i-use","title":"What version should I use?","text":"

    Use the exact version 0.2.0-dev. Do NOT use ^ or fuzzy versioning, as the API may change.

    "},{"location":"resources/faq/#how-do-i-configure-my-display","title":"How do I configure my display?","text":"

    Configure TFT_eSPI via build flags in platformio.ini. See Your First Project for examples.

    "},{"location":"resources/faq/#how-do-i-set-up-audio","title":"How do I set up audio?","text":"

    Choose an audio backend: - ESP32_DAC: Simple, one pin (GPIO 25 or 26) - ESP32_I2S: Higher quality, requires external DAC - SDL2_AudioBackend: For Native/PC development

    See Audio for details.

    "},{"location":"resources/faq/#development","title":"Development","text":""},{"location":"resources/faq/#how-do-i-create-a-scene","title":"How do I create a scene?","text":"

    Inherit from pixelroot32::core::Scene and implement init(), update(), and draw(). See Scenes and Entities.

    "},{"location":"resources/faq/#how-do-i-add-entities-to-a-scene","title":"How do I add entities to a scene?","text":"

    Create entities and call addEntity() in init(). The scene manages them automatically.

    "},{"location":"resources/faq/#whats-the-difference-between-entity-actor-and-physicsactor","title":"What's the difference between Entity, Actor, and PhysicsActor?","text":"
    • Entity: Base class, can be drawn and updated
    • Actor: Entity with collision detection
    • PhysicsActor: Actor with automatic physics (velocity, gravity, friction)

    See Scenes and Entities for details.

    "},{"location":"resources/faq/#how-do-i-handle-input","title":"How do I handle input?","text":"

    Access InputManager through the engine:

    auto& input = engine.getInputManager();\nif (input.isButtonPressed(Buttons::A)) {\n    // Handle input\n}\n

    See Input and Control.

    "},{"location":"resources/faq/#how-do-i-play-sounds","title":"How do I play sounds?","text":"

    Create an AudioEvent and play it:

    pixelroot32::audio::AudioEvent sound{};\nsound.type = pixelroot32::audio::WaveType::PULSE;\nsound.frequency = 800.0f;\nsound.duration = 0.1f;\nengine.getAudioEngine().playEvent(sound);\n

    See Audio.

    "},{"location":"resources/faq/#how-do-i-create-sprites","title":"How do I create sprites?","text":"

    Define sprite data manually or use the Sprite Compiler tool. See Sprites and Animation.

    "},{"location":"resources/faq/#can-i-use-images-instead-of-manual-sprite-data","title":"Can I use images instead of manual sprite data?","text":"

    Yes, use the Sprite Compiler tool to convert PNG images to sprite data. See Available Tools.

    "},{"location":"resources/faq/#performance","title":"Performance","text":""},{"location":"resources/faq/#why-is-my-game-running-slowly","title":"Why is my game running slowly?","text":"

    Common causes: - Too many entities (MAX_ENTITIES = 32) - Too many draw calls - Expensive calculations in update() - Memory issues

    See Performance Tuning for solutions.

    "},{"location":"resources/faq/#what-fps-should-i-target","title":"What FPS should I target?","text":"

    30-60 FPS is typical. Lower complexity games can achieve 60 FPS, more complex games may need to target 30 FPS.

    "},{"location":"resources/faq/#how-do-i-optimize-my-game","title":"How do I optimize my game?","text":"
    • Use object pooling
    • Implement viewport culling
    • Reduce entity count
    • Cache calculations
    • Use tilemaps for backgrounds

    See Performance Tuning.

    "},{"location":"resources/faq/#memory","title":"Memory","text":""},{"location":"resources/faq/#why-do-i-get-out-of-memory-errors","title":"Why do I get \"out of memory\" errors?","text":"

    ESP32 has limited RAM (~320KB). Solutions: - Use object pooling - Store data in flash (const/constexpr) - Reduce entity count - Avoid dynamic allocation

    See Memory Management.

    "},{"location":"resources/faq/#what-is-max_entities","title":"What is MAX_ENTITIES?","text":"

    MAX_ENTITIES = 32 is a hard limit per scene. This includes all entities: actors, UI elements, particles, etc.

    Solutions: - Use object pooling to reuse entities - Disable entities instead of removing - Combine multiple entities into one

    "},{"location":"resources/faq/#how-do-i-check-available-memory","title":"How do I check available memory?","text":"
    #ifdef PLATFORM_ESP32\nSerial.print(\"Free heap: \");\nSerial.println(ESP.getFreeHeap());\n#endif\n
    "},{"location":"resources/faq/#hardware","title":"Hardware","text":""},{"location":"resources/faq/#which-esp32-board-should-i-use","title":"Which ESP32 board should I use?","text":"

    Any ESP32 board works. Common choices: - ESP32-WROOM-32 - ESP32-WROVER (more RAM) - ESP32-DevKit

    "},{"location":"resources/faq/#which-display-should-i-use","title":"Which display should I use?","text":"

    Popular choices: - ST7789: 240x240, good quality - ST7735: 128x128, smaller/cheaper - ILI9341: 240x320, larger

    See Platforms and Drivers.

    "},{"location":"resources/faq/#how-many-buttons-do-i-need","title":"How many buttons do I need?","text":"

    Minimum: 4 (UP, DOWN, LEFT, RIGHT) Recommended: 6 (add A and B buttons) More buttons can be added if needed.

    "},{"location":"resources/faq/#can-i-use-analog-joysticks","title":"Can I use analog joysticks?","text":"

    Not directly supported. You can read analog pins manually and convert to digital input, but the engine expects digital buttons.

    "},{"location":"resources/faq/#troubleshooting","title":"Troubleshooting","text":""},{"location":"resources/faq/#my-display-is-blank-whats-wrong","title":"My display is blank. What's wrong?","text":"
    1. Check wiring connections
    2. Verify pin numbers in platformio.ini
    3. Lower SPI frequency
    4. Check display type matches hardware
    5. Verify power supply

    See Troubleshooting.

    "},{"location":"resources/faq/#buttons-dont-work-why","title":"Buttons don't work. Why?","text":"
    1. Check button wiring
    2. Verify pin numbers in InputConfig
    3. Check pull-up/pull-down resistors
    4. Ensure InputManager is being updated
    5. Test with isButtonDown() vs isButtonPressed()
    "},{"location":"resources/faq/#audio-is-distorted-how-do-i-fix-it","title":"Audio is distorted. How do I fix it?","text":"
    1. Lower volume levels
    2. Reduce sample rate (try 11025 Hz)
    3. Check for too many simultaneous sounds
    4. Verify hardware connections
    5. Check power supply
    "},{"location":"resources/faq/#game-crashes-randomly-whats-happening","title":"Game crashes randomly. What's happening?","text":"

    Common causes: - Out of memory - Too many entities - Infinite loops - Stack overflow - Watchdog timeout

    See Troubleshooting for debugging techniques.

    "},{"location":"resources/faq/#advanced","title":"Advanced","text":""},{"location":"resources/faq/#can-i-extend-the-engine","title":"Can I extend the engine?","text":"

    Yes, PixelRoot32 is designed to be extensible. You can: - Create custom display drivers - Create custom audio backends - Extend existing systems

    See Extensibility.

    "},{"location":"resources/faq/#can-i-use-3d-graphics","title":"Can I use 3D graphics?","text":"

    No, PixelRoot32 is 2D-only. It's designed for sprite-based 2D games.

    "},{"location":"resources/faq/#can-i-add-networking","title":"Can I add networking?","text":"

    Not currently supported. The engine focuses on single-player games.

    "},{"location":"resources/faq/#can-i-save-game-data","title":"Can I save game data?","text":"

    Not currently supported. A save system is planned for future versions.

    "},{"location":"resources/faq/#can-i-use-multiple-scenes-at-once","title":"Can I use multiple scenes at once?","text":"

    Yes, use SceneManager to push/pop scenes. This is useful for menus and pause screens.

    "},{"location":"resources/faq/#getting-help","title":"Getting Help","text":""},{"location":"resources/faq/#where-can-i-get-help","title":"Where can I get help?","text":"
    • Documentation: This documentation site
    • Examples: Study example games in the samples project
    • Discord: Community Discord server
    • GitHub: Open an issue for bugs
    "},{"location":"resources/faq/#how-do-i-report-a-bug","title":"How do I report a bug?","text":"

    Create a detailed bug report with: - Platform (ESP32 or Native) - Hardware details - Minimal reproduction code - Error messages - Expected vs actual behavior

    "},{"location":"resources/faq/#can-i-contribute","title":"Can I contribute?","text":"

    Yes! PixelRoot32 is open source. Check the main repository for contribution guidelines.

    "},{"location":"resources/faq/#see-also","title":"See Also","text":"
    • Troubleshooting - Detailed problem solving
    • Limitations and Considerations - What the engine can/can't do
    • Getting Started - Start here if you're new
    • Manual - Complete development guides

    Can't find your question? Check the Troubleshooting guide or ask on the Discord server.

    "},{"location":"resources/limitations_and_considerations/","title":"Limitations and Considerations","text":"

    This document honestly documents what PixelRoot32 can and cannot do, helping you make informed decisions about using the engine.

    "},{"location":"resources/limitations_and_considerations/#hardware-limitations-esp32","title":"Hardware Limitations (ESP32)","text":""},{"location":"resources/limitations_and_considerations/#memory-constraints","title":"Memory Constraints","text":"

    RAM: - Available: ~320KB total (varies by ESP32 model) - Heap: Limited and fragmented over time - Stack: ~8KB, avoid large stack allocations - Impact: Limits entity count, sprite data, and dynamic allocation

    Flash: - Available: 4MB+ (varies by model) - Usage: Program code, sprite data, assets - Impact: Large games may approach limits

    Recommendations: - Use object pooling - Store data in flash (const/constexpr) - Avoid dynamic allocation in game loop - Keep entity count low

    "},{"location":"resources/limitations_and_considerations/#cpu-limitations","title":"CPU Limitations","text":"

    Performance: - Clock Speed: 240MHz (typically) - Single-threaded: One core handles everything - Target FPS: 30-60 FPS (depends on complexity) - Frame Budget: ~16-33ms per frame at 60 FPS

    Impact: - Complex games may struggle - Many entities reduce performance - Expensive calculations hurt FPS

    Recommendations: - Optimize rendering - Reduce entity count - Cache calculations - Profile on hardware

    "},{"location":"resources/limitations_and_considerations/#display-limitations","title":"Display Limitations","text":"

    Supported Displays: - TFT displays via SPI (ST7735, ST7789, ILI9341, etc.) - Limited to SPI displays - Resolution typically 128x128 to 320x240

    Constraints: - SPI communication speed limits - Display initialization complexity - Power consumption

    "},{"location":"resources/limitations_and_considerations/#audio-limitations","title":"Audio Limitations","text":"

    Hardware: - Internal DAC: Lower quality, simple setup - I2S: Higher quality, requires external DAC - Sample rates: 11025 Hz (DAC) or 22050 Hz (I2S)

    Constraints: - 4 channels total (2 Pulse, 1 Triangle, 1 Noise) - Music uses one channel - Limited simultaneous sounds - Quality limited by hardware

    "},{"location":"resources/limitations_and_considerations/#software-limitations","title":"Software Limitations","text":""},{"location":"resources/limitations_and_considerations/#entity-system","title":"Entity System","text":"

    MAX_ENTITIES = 32 per scene - Hard limit, cannot be changed easily - Applies to all entities (actors, UI, particles, etc.) - Must manage entity count carefully

    Workarounds: - Use object pooling - Reuse entities - Disable entities instead of removing - Combine multiple entities into one

    "},{"location":"resources/limitations_and_considerations/#no-rtti-runtime-type-information","title":"No RTTI (Runtime Type Information)","text":"

    Impact: - Cannot use dynamic_cast in most code - Type checking must be done manually - Limits polymorphism patterns

    Alternatives: - Use virtual functions - Manual type checking - Tag-based systems

    "},{"location":"resources/limitations_and_considerations/#no-exceptions-in-critical-code","title":"No Exceptions in Critical Code","text":"

    Impact: - Cannot use try/catch in game loop - Error handling must be explicit - Crashes instead of exceptions

    Best Practices: - Validate inputs - Check return values - Use assertions for debugging - Handle errors explicitly

    "},{"location":"resources/limitations_and_considerations/#no-dynamic-allocation-in-game-loop","title":"No Dynamic Allocation in Game Loop","text":"

    Impact: - Cannot use new/delete during gameplay - Must pre-allocate resources - Limits flexibility

    Solutions: - Object pooling - Pre-allocation in init() - Static buffers - Fixed-size arrays

    "},{"location":"resources/limitations_and_considerations/#no-advanced-features","title":"No Advanced Features","text":"

    Not Supported: - 3D graphics - Shaders - Advanced physics (joints, constraints) - Networking - File system (ESP32) - Advanced audio effects

    Focus: - 2D sprite-based games - Simple physics - Retro-style games - Embedded-friendly features

    "},{"location":"resources/limitations_and_considerations/#experimental-features","title":"Experimental Features","text":""},{"location":"resources/limitations_and_considerations/#2bpp-sprites","title":"2bpp Sprites","text":"

    Status: Experimental - Requires PIXELROOT32_ENABLE_2BPP_SPRITES flag - May have bugs or limitations - Not fully tested

    Use with caution: - Test thoroughly - May change in future versions - Report issues if found

    "},{"location":"resources/limitations_and_considerations/#4bpp-sprites","title":"4bpp Sprites","text":"

    Status: Experimental - Requires PIXELROOT32_ENABLE_4BPP_SPRITES flag - More experimental than 2bpp - Higher memory usage

    Use with caution: - Test extensively - Monitor memory usage - May be unstable

    "},{"location":"resources/limitations_and_considerations/#scene-arena","title":"Scene Arena","text":"

    Status: Experimental - Requires PIXELROOT32_ENABLE_SCENE_ARENA flag - Alternative memory management - May have bugs

    Recommendations: - Use object pooling instead (more stable) - Test thoroughly if using - May be removed or changed

    "},{"location":"resources/limitations_and_considerations/#unsupported-features-current","title":"Unsupported Features (Current)","text":""},{"location":"resources/limitations_and_considerations/#planned-but-not-available","title":"Planned but Not Available","text":"
    • u8g2 Driver: Alternative display driver (planned)
    • Music Compiler: Tool to convert music files (planned)
    • Tilemap Compiler: Tool to create tilemaps (planned)
    • Save System: Persistent storage system (planned)
    • Spatial Partitioning: Advanced collision optimization (planned)
    "},{"location":"resources/limitations_and_considerations/#not-planned","title":"Not Planned","text":"
    • 3D Graphics: 2D-only engine
    • Networking: No network support
    • File System: No file I/O on ESP32
    • Advanced Audio: NES-like audio only
    • Scripting: No Lua/JavaScript support
    "},{"location":"resources/limitations_and_considerations/#best-practices-for-esp32","title":"Best Practices for ESP32","text":""},{"location":"resources/limitations_and_considerations/#memory-management","title":"Memory Management","text":"
    • Pre-allocate: All resources in init()
    • Object pooling: Reuse entities
    • Flash storage: Use const/constexpr for data
    • Avoid strings: Use static buffers
    • Monitor usage: Check heap regularly
    "},{"location":"resources/limitations_and_considerations/#performance","title":"Performance","text":"
    • Limit entities: Stay well below MAX_ENTITIES
    • Optimize rendering: Use culling, batching
    • Cache calculations: Avoid repeated work
    • Profile on hardware: PC performance \u2260 ESP32
    "},{"location":"resources/limitations_and_considerations/#development","title":"Development","text":"
    • Test on hardware: Don't rely only on Native
    • Start simple: Add complexity gradually
    • Monitor memory: Watch for leaks
    • Optimize incrementally: Profile and optimize
    "},{"location":"resources/limitations_and_considerations/#what-pixelroot32-is-good-for","title":"What PixelRoot32 IS Good For","text":"

    \u2705 Retro-style 2D games \u2705 Arcade games \u2705 Puzzle games \u2705 Platformers \u2705 Shooters \u2705 Educational projects \u2705 Prototyping \u2705 Embedded game development

    "},{"location":"resources/limitations_and_considerations/#what-pixelroot32-is-not-good-for","title":"What PixelRoot32 is NOT Good For","text":"

    \u274c 3D games \u274c Complex physics simulations \u274c Large open worlds \u274c Games requiring many entities \u274c Games with complex graphics \u274c Network multiplayer \u274c Games requiring file I/O

    "},{"location":"resources/limitations_and_considerations/#making-informed-decisions","title":"Making Informed Decisions","text":""},{"location":"resources/limitations_and_considerations/#before-starting-a-project","title":"Before Starting a Project","text":"
    1. Assess requirements: Does PixelRoot32 fit?
    2. Check limitations: Can you work within constraints?
    3. Plan architecture: Design around limitations
    4. Test early: Verify on hardware early
    "},{"location":"resources/limitations_and_considerations/#if-limitations-are-a-problem","title":"If Limitations Are a Problem","text":"

    Consider alternatives: - Full game engines (Unity, Godot) for complex games - Custom solutions for specific needs - Different hardware for more resources

    Or work within limits: - Simplify game design - Optimize aggressively - Use creative solutions

    "},{"location":"resources/limitations_and_considerations/#version-compatibility","title":"Version Compatibility","text":""},{"location":"resources/limitations_and_considerations/#current-version","title":"Current Version","text":"
    • Engine Version: 0.2.0-dev
    • API Stability: May change
    • Breaking Changes: Possible in future versions

    Recommendations: - Pin exact version in platformio.ini - Don't use ^ or fuzzy versioning - Test after engine updates - Review changelog

    "},{"location":"resources/limitations_and_considerations/#honest-assessment","title":"Honest Assessment","text":"

    PixelRoot32 is designed for: - Simple to medium complexity games - Retro/arcade style - ESP32 hardware constraints - Rapid development

    It is not designed for: - AAA game complexity - Modern graphics - Large-scale games - Unlimited resources

    "},{"location":"resources/limitations_and_considerations/#see-also","title":"See Also","text":"
    • Memory Management - Working with memory limits
    • Performance Tuning - Optimizing performance
    • Troubleshooting - Solving problems
    • FAQ - Common questions

    Remember: Understanding limitations helps you build better games within PixelRoot32's capabilities.

    "},{"location":"resources/troubleshooting/","title":"Troubleshooting","text":"

    This guide helps you diagnose and fix common issues when developing with PixelRoot32.

    "},{"location":"resources/troubleshooting/#compilation-problems","title":"Compilation Problems","text":""},{"location":"resources/troubleshooting/#common-compilation-errors","title":"Common Compilation Errors","text":"

    Error: Library not found

    Solution: Ensure PixelRoot32-Game-Engine is properly installed\n- Check platformio.ini has correct lib_deps\n- Verify library version matches (use exact version, not ^)\n- Try: pio lib install\n

    Error: Include file not found

    Solution: Check include paths\n- Verify lib_extra_dirs in platformio.ini\n- Check that library is in lib/ directory\n- Ensure correct namespace (pixelroot32::)\n

    Error: Undefined reference

    Solution: Link missing libraries\n- Check all required libraries are listed\n- Verify TFT_eSPI is installed for ESP32\n- Check SDL2 is installed for Native builds\n

    Error: Build flags not recognized

    Solution: Verify build flag syntax\n- Use -D FLAG_NAME (not --define)\n- Check flag names are correct\n- Ensure flags are in correct environment section\n

    "},{"location":"resources/troubleshooting/#configuration-issues","title":"Configuration Issues","text":"

    Wrong display type: - Verify DisplayType matches your hardware - Check TFT_eSPI build flags match display - Test with different display types

    Incorrect pin configuration: - Verify GPIO pins match your wiring - Check pin numbers in platformio.ini - Ensure pins aren't used by other peripherals

    "},{"location":"resources/troubleshooting/#hardware-problems-esp32","title":"Hardware Problems (ESP32)","text":""},{"location":"resources/troubleshooting/#display-not-working","title":"Display Not Working","text":"

    Symptoms: - Blank screen - Garbled display - No output

    Solutions: 1. Check wiring: - Verify SPI connections (MOSI, SCLK, DC, RST) - Check power supply (3.3V or 5V as required) - Ensure ground connections

    1. Verify configuration:
    2. Check display type matches hardware
    3. Verify pin numbers in platformio.ini
    4. Test with known working configuration

    5. SPI frequency:

    6. Lower SPI frequency (try 20MHz instead of 40MHz)
    7. Some displays need slower speeds
    8. Check display datasheet for max frequency

    9. Display initialization:

    10. Try different rotation values
    11. Check display width/height settings
    12. Verify TFT_eSPI driver is correct
    "},{"location":"resources/troubleshooting/#buttons-not-responding","title":"Buttons Not Responding","text":"

    Symptoms: - No input detected - Buttons don't trigger actions - Input feels laggy

    Solutions: 1. Check wiring: - Verify button connections to GPIO pins - Check pull-up/pull-down resistors - Test buttons with multimeter

    1. Verify pin configuration:
    2. Check InputConfig pin numbers
    3. Ensure pins match hardware
    4. Verify pins aren't used elsewhere

    5. Input debouncing:

    6. Add hardware debouncing (capacitor)
    7. Check InputManager is being updated
    8. Verify input is read in update(), not draw()

    9. Button logic:

    10. Test with isButtonDown() vs isButtonPressed()
    11. Check button indices match configuration
    12. Verify input is accessed correctly
    "},{"location":"resources/troubleshooting/#audio-not-working","title":"Audio Not Working","text":"

    Symptoms: - No sound output - Distorted audio - Audio glitches

    Solutions: 1. DAC Configuration: - Verify DAC pin (25 or 26 for ESP32) - Check sample rate (11025 Hz recommended) - Ensure audio backend is initialized

    1. I2S Configuration:
    2. Verify I2S pin connections (BCLK, LRCK, DOUT)
    3. Check external DAC is powered
    4. Verify I2S DAC is compatible

    5. Audio quality:

    6. Lower sample rate if distorted
    7. Reduce volume levels
    8. Check for too many simultaneous sounds
    9. Verify audio buffer size

    10. Hardware:

    11. Check speaker connections
    12. Verify amplifier is powered
    13. Test with different audio hardware
    14. Check audio cable connections
    "},{"location":"resources/troubleshooting/#power-issues","title":"Power Issues","text":"

    Symptoms: - ESP32 resets randomly - Display flickers - Unstable operation

    Solutions: 1. Power supply: - Use adequate power supply (500mA+ recommended) - Check voltage is stable (3.3V) - Add decoupling capacitors

    1. Current draw:
    2. Display draws significant current
    3. Audio amplifier adds load
    4. Reduce brightness if possible

    5. Wiring:

    6. Use thick wires for power
    7. Keep power wires short
    8. Add capacitors near ESP32
    "},{"location":"resources/troubleshooting/#performance-problems","title":"Performance Problems","text":""},{"location":"resources/troubleshooting/#low-fps","title":"Low FPS","text":"

    Symptoms: - Game runs slowly - Laggy movement - Stuttering

    Solutions: 1. Reduce entity count: - Limit active entities (MAX_ENTITIES = 32) - Disable off-screen entities - Use object pooling

    1. Optimize rendering:
    2. Use viewport culling
    3. Reduce draw calls
    4. Use tilemaps instead of individual sprites
    5. Limit sprite count

    6. Simplify logic:

    7. Cache expensive calculations
    8. Reduce collision checks
    9. Lower update frequency for non-critical entities

    10. Check hardware:

    11. Verify ESP32 is running at full speed (240MHz)
    12. Check for thermal throttling
    13. Ensure adequate power supply
    "},{"location":"resources/troubleshooting/#frame-drops","title":"Frame Drops","text":"

    Symptoms: - Occasional stuttering - Inconsistent frame times - Periodic freezes

    Solutions: 1. Identify bottlenecks: - Profile frame time - Check for expensive operations - Look for blocking code

    1. Optimize update loop:
    2. Avoid dynamic allocation
    3. Cache calculations
    4. Reduce string operations

    5. Memory issues:

    6. Check for memory leaks
    7. Reduce memory usage
    8. Use object pooling
    "},{"location":"resources/troubleshooting/#freezescrashes","title":"Freezes/Crashes","text":"

    Symptoms: - Game stops responding - ESP32 resets - Watchdog resets

    Solutions: 1. Memory issues: - Check available heap memory - Reduce entity count - Avoid dynamic allocation - Use object pooling

    1. Infinite loops:
    2. Check for infinite loops in update()
    3. Verify collision detection doesn't loop
    4. Check animation logic

    5. Stack overflow:

    6. Avoid large stack allocations
    7. Reduce recursion depth
    8. Move large data to heap (carefully)

    9. Watchdog:

    10. Ensure update() completes quickly
    11. Add yield() calls if needed
    12. Check for blocking operations
    "},{"location":"resources/troubleshooting/#memory-problems","title":"Memory Problems","text":""},{"location":"resources/troubleshooting/#out-of-memory","title":"Out of Memory","text":"

    Symptoms: - Compilation fails - Runtime crashes - \"Allocation failed\" errors

    Solutions: 1. Reduce memory usage: - Use 1bpp sprites instead of 2bpp/4bpp - Store data in flash (const/constexpr) - Reduce entity count - Use object pooling

    1. Optimize data:
    2. Reuse sprites
    3. Compress tilemap data
    4. Remove unused code/data

    5. Memory management:

    6. Avoid dynamic allocation in game loop
    7. Pre-allocate all resources
    8. Use static buffers
    "},{"location":"resources/troubleshooting/#memory-fragmentation","title":"Memory Fragmentation","text":"

    Symptoms: - Gradual performance degradation - Allocation failures over time - Crashes after running for a while

    Solutions: 1. Use object pooling: - Pre-allocate entities - Reuse objects instead of creating/destroying - Avoid frequent new/delete

    1. Pre-allocate resources:
    2. Allocate everything in init()
    3. Use fixed-size arrays
    4. Avoid dynamic containers
    "},{"location":"resources/troubleshooting/#native-build-problems","title":"Native Build Problems","text":""},{"location":"resources/troubleshooting/#sdl2-not-found","title":"SDL2 Not Found","text":"

    Symptoms: - Compilation fails - Linker errors - Missing SDL2 symbols

    Solutions: 1. Install SDL2: - Windows (MSYS2): pacman -S mingw-w64-x86_64-SDL2 - Linux: sudo apt-get install libsdl2-dev - macOS: brew install sdl2

    1. Check paths:
    2. Verify include paths in platformio.ini
    3. Check library paths
    4. Ensure SDL2 version is compatible

    5. Linker flags:

    6. Verify -lSDL2 is in build flags
    7. Check library search paths
    8. Ensure SDL2 DLL is accessible (Windows)
    "},{"location":"resources/troubleshooting/#window-not-opening","title":"Window Not Opening","text":"

    Symptoms: - Program runs but no window - Console shows errors - Immediate exit

    Solutions: 1. Check SDL2 initialization: - Verify SDL2 is properly initialized - Check for SDL2 error messages - Ensure display config is correct

    1. Graphics drivers:
    2. Update graphics drivers
    3. Check SDL2 video backend
    4. Test with simple SDL2 program

    5. Console output:

    6. Run from terminal to see errors
    7. Check for error messages
    8. Verify SDL2 is working
    "},{"location":"resources/troubleshooting/#debugging-techniques","title":"Debugging Techniques","text":""},{"location":"resources/troubleshooting/#serial-debugging-esp32","title":"Serial Debugging (ESP32)","text":"
    void setup() {\n    Serial.begin(115200);\n    // ... initialization\n\n    Serial.println(\"Engine initialized\");\n}\n\nvoid update(unsigned long deltaTime) override {\n    // Debug output\n    if (frameCount % 60 == 0) {\n        Serial.print(\"FPS: \");\n        Serial.println(1000.0f / deltaTime);\n    }\n    frameCount++;\n}\n
    "},{"location":"resources/troubleshooting/#memory-monitoring","title":"Memory Monitoring","text":"
    #ifdef PLATFORM_ESP32\n#include <Arduino.h>\n\nvoid checkMemory() {\n    Serial.print(\"Free heap: \");\n    Serial.println(ESP.getFreeHeap());\n    Serial.print(\"Largest block: \");\n    Serial.println(ESP.getMaxAllocHeap());\n}\n#endif\n
    "},{"location":"resources/troubleshooting/#performance-profiling","title":"Performance Profiling","text":"
    class Profiler {\nprivate:\n    unsigned long updateTime = 0;\n    unsigned long drawTime = 0;\n\npublic:\n    void startUpdate() {\n        updateTime = micros();\n    }\n\n    void endUpdate() {\n        updateTime = micros() - updateTime;\n    }\n\n    void log() {\n        Serial.print(\"Update: \");\n        Serial.print(updateTime);\n        Serial.print(\"us, Draw: \");\n        Serial.println(drawTime);\n    }\n};\n
    "},{"location":"resources/troubleshooting/#visual-debugging","title":"Visual Debugging","text":"
    • Draw hitboxes: Visualize collision boxes
    • Show FPS: Display frame rate on screen
    • Entity count: Show active entity count
    • Memory usage: Display memory statistics
    "},{"location":"resources/troubleshooting/#common-patterns-for-debugging","title":"Common Patterns for Debugging","text":""},{"location":"resources/troubleshooting/#isolate-the-problem","title":"Isolate the Problem","text":"
    1. Minimal reproduction: Create smallest code that shows issue
    2. Disable features: Turn off systems one by one
    3. Test incrementally: Add features back one at a time
    "},{"location":"resources/troubleshooting/#check-the-basics","title":"Check the Basics","text":"
    1. Verify initialization: Ensure engine.init() is called
    2. Check scene setup: Verify scene is set and initialized
    3. Test on both platforms: Compare ESP32 vs Native behavior
    4. Review recent changes: What changed before the issue?
    "},{"location":"resources/troubleshooting/#use-logging","title":"Use Logging","text":"
    #define DEBUG_MODE\n\n#ifdef DEBUG_MODE\n    #define DEBUG_LOG(x) Serial.println(x)\n#else\n    #define DEBUG_LOG(x)\n#endif\n\n// Usage\nDEBUG_LOG(\"Entity created\");\nDEBUG_LOG(\"Collision detected\");\n
    "},{"location":"resources/troubleshooting/#getting-help","title":"Getting Help","text":"

    If you can't resolve an issue:

    1. Check documentation: Review relevant guides
    2. Search examples: Look at example games
    3. Review code: Check engine source code
    4. Community: Ask on Discord or GitHub
    5. Report issue: Create detailed bug report
    "},{"location":"resources/troubleshooting/#bug-report-template","title":"Bug Report Template","text":"

    When reporting issues, include:

    • Platform: ESP32 or Native
    • Hardware: Display type, ESP32 model
    • Code: Minimal reproduction code
    • Error messages: Full error output
    • Expected behavior: What should happen
    • Actual behavior: What actually happens
    • Steps to reproduce: How to trigger the issue
    "},{"location":"resources/troubleshooting/#see-also","title":"See Also","text":"
    • Limitations and Considerations - Known limitations
    • Performance Tuning - Performance optimization
    • Memory Management - Memory optimization
    • FAQ - Frequently asked questions

    Note: Many issues are configuration-related. Double-check your setup before assuming a bug.

    "},{"location":"tools/sprite_compiler/advanced_features/","title":"Sprite Compiler Advanced Features","text":"

    Advanced features and options for the PixelRoot32 Sprite Compiler to optimize sprite conversion and handle complex scenarios.

    "},{"location":"tools/sprite_compiler/advanced_features/#automatic-palette-detection","title":"Automatic Palette Detection","text":"

    The Sprite Compiler automatically detects if your sprite uses one of the engine's built-in palettes. This simplifies your workflow and ensures color consistency.

    "},{"location":"tools/sprite_compiler/advanced_features/#predefined-engine-palettes","title":"Predefined Engine Palettes","text":"

    The engine includes 5 optimized palettes: - PR32 (Default PixelRoot32 palette) - NES (Nintendo style) - GB (GameBoy style) - GBC (GameBoy Color style) - PICO8 (Fantasy console style)

    "},{"location":"tools/sprite_compiler/advanced_features/#how-it-works","title":"How it works","text":"
    1. Detection: When you compile an image, the tool compares all unique colors found in the sprite with the colors in the 5 engine palettes.
    2. Match: If all colors in your sprite belong to one of these palettes, the compiler:
    3. Omits generating a color array in the header.
    4. Assumes you will use the engine's built-in palette definitions at runtime.
    5. Custom Palette: If your sprite uses colors not found in the engine palettes, it automatically generates a {PREFIX}_PALETTE_MAPPING[16] array in the header file.
    "},{"location":"tools/sprite_compiler/advanced_features/#naming-with-prefixes","title":"Naming with Prefixes","text":"

    You can organize your generated code by using the --prefix parameter (or the Prefix field in the GUI).

    "},{"location":"tools/sprite_compiler/advanced_features/#using-prefixes","title":"Using Prefixes","text":"

    By default, sprites are named SPRITE_N_.... Using a prefix allows you to create more descriptive names and avoid naming collisions.

    python main.py sheet.png --grid 16x16 --sprite 0,0,1,1 --prefix PLAYER_JUM\n

    Generated names will follow this pattern: - PLAYER_JUM_SPRITE_0_LAYER_0 - PLAYER_JUM_SPRITE_0_2BPP - PLAYER_JUM_SPRITE_0_4BPP - PLAYER_JUM_PALETTE_MAPPING (if a custom palette is used)

    "},{"location":"tools/sprite_compiler/advanced_features/#export-modes","title":"Export Modes","text":""},{"location":"tools/sprite_compiler/advanced_features/#layered-1bpp","title":"Layered (1bpp)","text":"

    Best for standard PixelRoot32 rendering. It extracts each color into its own bitmask (1bpp). The engine then renders these layers sequentially.

    "},{"location":"tools/sprite_compiler/advanced_features/#packed-2bpp-4bpp","title":"Packed (2bpp / 4bpp)","text":"

    Generates a single packed array where each pixel uses multiple bits. - 2bpp: 4 colors max (Index 0 is always transparent). - 4bpp: 16 colors max (Index 0 is always transparent).

    These modes are more efficient for sprites with many colors as they require only a single draw call.

    "},{"location":"tools/sprite_compiler/installation/","title":"Sprite Compiler Installation","text":"

    This guide walks you through installing the PixelRoot32 Sprite Compiler on your system.

    "},{"location":"tools/sprite_compiler/installation/#prerequisites","title":"Prerequisites","text":""},{"location":"tools/sprite_compiler/installation/#required-software","title":"Required Software","text":"
    • Python: Version 3.8 or higher
    • pip: Usually included with Python
    "},{"location":"tools/sprite_compiler/installation/#verify-prerequisites","title":"Verify Prerequisites","text":"

    Check if Python is installed:

    python --version\n# Should show 3.8.0 or higher\n

    Check if pip is installed:

    pip --version\n# Should show version number\n

    If not installed, download from python.org

    "},{"location":"tools/sprite_compiler/installation/#installation-methods","title":"Installation Methods","text":""},{"location":"tools/sprite_compiler/installation/#method-1-from-source-recommended","title":"Method 1: From Source (Recommended)","text":""},{"location":"tools/sprite_compiler/installation/#step-1-clone-repository","title":"Step 1: Clone Repository","text":"
    git clone https://github.com/Gperez88/PixelRoot32-Sprite-Compiler.git\ncd PixelRoot32-Sprite-Compiler\n
    "},{"location":"tools/sprite_compiler/installation/#step-2-install-dependencies","title":"Step 2: Install Dependencies","text":"
    pip install -r requirements.txt\n
    "},{"location":"tools/sprite_compiler/installation/#step-3-verify-installation","title":"Step 3: Verify Installation","text":"
    python main.py --help\n
    "},{"location":"tools/sprite_compiler/installation/#method-2-standalone-executable-windows","title":"Method 2: Standalone Executable (Windows)","text":"

    If you are on Windows, you can download the latest standalone .exe from the Releases section of the repository. This does not require Python or any dependencies to be installed.

    "},{"location":"tools/sprite_compiler/installation/#platform-specific-instructions","title":"Platform-Specific Instructions","text":""},{"location":"tools/sprite_compiler/installation/#windows","title":"Windows","text":""},{"location":"tools/sprite_compiler/installation/#using-npm-recommended","title":"Using npm (Recommended)","text":"
    1. Install Node.js from nodejs.org
    2. Download the Windows installer
    3. Run the installer
    4. Restart your terminal/command prompt

    5. Open Command Prompt or PowerShell

    6. Install globally:

      npm install -g pr32-sprite-compiler\n

    7. Verify:

      pr32-sprite-compiler --version\n

    "},{"location":"tools/sprite_compiler/installation/#troubleshooting-windows-issues","title":"Troubleshooting Windows Issues","text":"

    \"pr32-sprite-compiler is not recognized\": - Ensure Node.js is in your PATH - Restart terminal after installation - Try using full path: C:\\Users\\YourName\\AppData\\Roaming\\npm\\pr32-sprite-compiler.cmd

    Permission errors: - Run terminal as Administrator - Or install locally: npm install pr32-sprite-compiler (without -g)

    "},{"location":"tools/sprite_compiler/installation/#linux","title":"Linux","text":""},{"location":"tools/sprite_compiler/installation/#using-npm","title":"Using npm","text":"
    1. Install Node.js (if not already installed):

    Ubuntu/Debian:

    curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -\nsudo apt-get install -y nodejs\n

    Fedora/RHEL:

    sudo dnf install nodejs npm\n

    1. Install Sprite Compiler:

      sudo npm install -g pr32-sprite-compiler\n

    2. Verify:

      pr32-sprite-compiler --version\n

    "},{"location":"tools/sprite_compiler/installation/#troubleshooting-linux-issues","title":"Troubleshooting Linux Issues","text":"

    Permission denied: - Use sudo for global installation - Or install locally without -g flag

    Command not found: - Check npm global bin path: npm config get prefix - Add to PATH if needed: export PATH=$PATH:$(npm config get prefix)/bin

    "},{"location":"tools/sprite_compiler/installation/#macos","title":"macOS","text":""},{"location":"tools/sprite_compiler/installation/#using-npm_1","title":"Using npm","text":"
    1. Install Node.js:
    2. Download from nodejs.org
    3. Or use Homebrew: brew install node

    4. Install Sprite Compiler:

      npm install -g pr32-sprite-compiler\n

    5. Verify:

      pr32-sprite-compiler --version\n

    "},{"location":"tools/sprite_compiler/installation/#using-homebrew-alternative","title":"Using Homebrew (Alternative)","text":"

    If available as a Homebrew formula:

    brew install pr32-sprite-compiler\n

    "},{"location":"tools/sprite_compiler/installation/#gui-version-installation","title":"GUI Version Installation","text":"

    If a GUI version is available:

    "},{"location":"tools/sprite_compiler/installation/#windows_1","title":"Windows","text":"

    Download the installer from the releases page and run it.

    "},{"location":"tools/sprite_compiler/installation/#linux_1","title":"Linux","text":"
    # Download AppImage or .deb package\n# Make executable and run\nchmod +x pr32-sprite-compiler-gui.AppImage\n./pr32-sprite-compiler-gui.AppImage\n
    "},{"location":"tools/sprite_compiler/installation/#macos_1","title":"macOS","text":"

    Download the .dmg file from releases and install.

    "},{"location":"tools/sprite_compiler/installation/#verification","title":"Verification","text":"

    After installation, verify everything works:

    "},{"location":"tools/sprite_compiler/installation/#test-basic-functionality","title":"Test Basic Functionality","text":"
    1. Create a test image:
    2. Create an 8x8 pixel PNG image (black and white)
    3. Save as test.png

    4. Run compiler:

      pr32-sprite-compiler test.png test_output.h\n

    5. Check output:

    6. File test_output.h should be created
    7. Should contain sprite data arrays
    "},{"location":"tools/sprite_compiler/installation/#check-version","title":"Check Version","text":"
    pr32-sprite-compiler --version\n
    "},{"location":"tools/sprite_compiler/installation/#check-help","title":"Check Help","text":"
    pr32-sprite-compiler --help\n
    "},{"location":"tools/sprite_compiler/installation/#updating","title":"Updating","text":""},{"location":"tools/sprite_compiler/installation/#update-via-npm","title":"Update via npm","text":"
    npm update -g pr32-sprite-compiler\n
    "},{"location":"tools/sprite_compiler/installation/#update-from-source","title":"Update from Source","text":"
    cd pr32-sprite-compiler\ngit pull\nnpm install\n
    "},{"location":"tools/sprite_compiler/installation/#uninstallation","title":"Uninstallation","text":""},{"location":"tools/sprite_compiler/installation/#remove-global-installation","title":"Remove Global Installation","text":"
    npm uninstall -g pr32-sprite-compiler\n
    "},{"location":"tools/sprite_compiler/installation/#remove-local-installation","title":"Remove Local Installation","text":"
    npm uninstall pr32-sprite-compiler\n
    "},{"location":"tools/sprite_compiler/installation/#troubleshooting","title":"Troubleshooting","text":""},{"location":"tools/sprite_compiler/installation/#common-issues","title":"Common Issues","text":"

    \"Command not found\" after installation: - Restart your terminal - Check npm global bin path: npm config get prefix - Verify PATH includes npm bin directory

    Permission errors: - On Linux/macOS: Use sudo for global install - Or install locally without -g flag - On Windows: Run terminal as Administrator

    Module not found errors: - Reinstall: npm install -g pr32-sprite-compiler - Clear npm cache: npm cache clean --force

    Version conflicts: - Check Node.js version: node --version - Update Node.js if version is too old - Use nvm (Node Version Manager) to manage versions

    "},{"location":"tools/sprite_compiler/installation/#getting-help","title":"Getting Help","text":"
    • Check the Usage Guide for usage examples
    • Review Troubleshooting for common issues
    • Open an issue on GitHub if problems persist
    "},{"location":"tools/sprite_compiler/installation/#next-steps","title":"Next Steps","text":"

    Once installed, proceed to: - Usage Guide - Learn how to use the compiler - Advanced Features - Explore advanced options

    "},{"location":"tools/sprite_compiler/installation/#see-also","title":"See Also","text":"
    • Overview - What the Sprite Compiler does
    • Available Tools - All PixelRoot32 tools
    "},{"location":"tools/sprite_compiler/overview/","title":"Sprite Compiler Overview","text":"

    The Sprite Compiler is a tool that converts PNG images into PixelRoot32 sprite data formats. It provides both a graphical interface (GUI) and a command-line interface (CLI) to automate the process of creating sprite arrays from image files.

    "},{"location":"tools/sprite_compiler/overview/#what-it-does","title":"What It Does","text":"

    The Sprite Compiler takes bitmap images (PNG) and converts them into C header files containing:

    • Sprite data arrays: Optimized uint16_t arrays for various formats.
    • Layered support: Generates multiple 1bpp layers for complex sprites.
    • Packed formats: Supports 2bpp and 4bpp packed formats.
    • Sprite sheets: Handles grid-based sprite sheets with auto-detection.
    "},{"location":"tools/sprite_compiler/overview/#key-features","title":"Key Features","text":""},{"location":"tools/sprite_compiler/overview/#format-support","title":"\u2705 Format Support","text":"
    • Layered (1bpp): Standard format, generates one array per color.
    • 2bpp (4 colors): Packed format, 2 bits per pixel.
    • 4bpp (16 colors): Packed format, 4 bits per pixel.
    "},{"location":"tools/sprite_compiler/overview/#gui-cli","title":"\u2705 GUI & CLI","text":"
    • Modern GUI: Step-by-step card-based interface for easy configuration.
    • Powerful CLI: Perfect for build scripts and automation.
    "},{"location":"tools/sprite_compiler/overview/#sprite-sheets","title":"\u2705 Sprite Sheets","text":"

    Automatically split sprite sheets into individual sprites:

    python main.py sheet.png --grid 16x16 --sprite 0,0,1,1 --sprite 1,0,1,1 --out output.h\n

    "},{"location":"tools/sprite_compiler/overview/#gui-interface","title":"GUI Interface","text":"

    The GUI is designed to be intuitive and follows a 5-step process:

    1. Input Image: Select your PNG source.
    2. Grid Settings: Define the cell size and offsets.
    3. Sprite Selection: Pick which cells to export.
    4. Export Settings: Choose the mode (Layered, 2bpp, 4bpp), set a Prefix, and choose the output path.
    5. About: Quick access to version info and credits (via the ? button).
    6. Log: Technical feedback and performance alerts.
    "},{"location":"tools/sprite_compiler/overview/#input-requirements","title":"Input Requirements","text":""},{"location":"tools/sprite_compiler/overview/#supported-formats","title":"Supported Formats","text":"
    • PNG: Primary format (recommended)
    • Indexed color PNG: Best for 1bpp conversion
    • Grayscale PNG: Automatically converted to 1bpp
    • RGB PNG: Converted using threshold or palette
    "},{"location":"tools/sprite_compiler/overview/#image-constraints","title":"Image Constraints","text":"

    For 1bpp sprites: - Maximum width: 16 pixels - Height: Any (typically 8, 16, 32 pixels) - Colors: Black and white (or converted automatically)

    For 2bpp sprites: - Maximum width: 16 pixels - Colors: Up to 4 colors

    For 4bpp sprites: - Maximum width: 16 pixels - Colors: Up to 16 colors

    "},{"location":"tools/sprite_compiler/overview/#output-format","title":"Output Format","text":"

    The compiler generates C header files with optimized arrays:

    // Generated by PixelRoot32 Sprite Compiler\n\n// Optional palette mapping if using custom colors\nstatic const Color PLAYER_PALETTE_MAPPING[16] = {\n    (Color)0, (Color)1, (Color)2, (Color)3,\n    // ...\n};\n\n// Sprite data array (4bpp example)\nstatic const uint16_t PLAYER_SPRITE_0_4BPP[] = {\n    0x0000, 0x1234, 0x5678, // Row 0\n    // ... more rows\n};\n
    "},{"location":"tools/sprite_compiler/overview/#use-cases","title":"Use Cases","text":""},{"location":"tools/sprite_compiler/overview/#1-single-sprite-conversion","title":"1. Single Sprite Conversion","text":"

    Convert a single image to a sprite:

    pr32-sprite-compiler player.png player_sprite.h --name PLAYER_SPRITE\n

    "},{"location":"tools/sprite_compiler/overview/#2-animation-frames","title":"2. Animation Frames","text":"

    Convert multiple frames for animation:

    pr32-sprite-compiler --batch walk_*.png --output-dir animations/ --prefix WALK_\n

    "},{"location":"tools/sprite_compiler/overview/#3-sprite-sheet-processing","title":"3. Sprite Sheet Processing","text":"

    Split a sprite sheet into individual sprites:

    pr32-sprite-compiler characters.png output.h --sheet 16x16 --count 8\n

    "},{"location":"tools/sprite_compiler/overview/#4-batch-asset-processing","title":"4. Batch Asset Processing","text":"

    Process entire asset directories:

    pr32-sprite-compiler --batch assets/sprites/*.png --output-dir src/sprites/\n

    "},{"location":"tools/sprite_compiler/overview/#workflow-integration","title":"Workflow Integration","text":""},{"location":"tools/sprite_compiler/overview/#typical-development-workflow","title":"Typical Development Workflow","text":"
    1. Create sprites in your image editor (Aseprite, Piskel, GIMP, etc.)
    2. Save as PNG with appropriate dimensions
    3. Run compiler to generate header files
    4. Include headers in your PixelRoot32 project
    5. Use sprites in your game code
    "},{"location":"tools/sprite_compiler/overview/#automation-example","title":"Automation Example","text":"
    #!/bin/bash\n# build-sprites.sh\n\n# Compile all sprites\npr32-sprite-compiler assets/sprites/*.png --output-dir src/sprites/\n\n# Continue with your build process\nplatformio run\n
    "},{"location":"tools/sprite_compiler/overview/#advantages-over-manual-creation","title":"Advantages Over Manual Creation","text":""},{"location":"tools/sprite_compiler/overview/#time-saving","title":"\u2705 Time Saving","text":"
    • No manual bit pattern conversion
    • Automatic format optimization
    • Batch processing multiple sprites
    "},{"location":"tools/sprite_compiler/overview/#accuracy","title":"\u2705 Accuracy","text":"
    • Correct bit ordering
    • Proper array formatting
    • Valid C++ syntax
    "},{"location":"tools/sprite_compiler/overview/#consistency","title":"\u2705 Consistency","text":"
    • Uniform naming conventions
    • Standardized output format
    • Consistent code structure
    "},{"location":"tools/sprite_compiler/overview/#maintainability","title":"\u2705 Maintainability","text":"
    • Easy to regenerate from source images
    • Version control friendly
    • Clear separation of assets and code
    "},{"location":"tools/sprite_compiler/overview/#limitations","title":"Limitations","text":"
    • Width limit: 16 pixels for 1bpp (hardware constraint)
    • Color depth: Limited by format (1bpp = 2 colors, 2bpp = 4 colors, etc.)
    • File format: Primarily PNG (other formats may require conversion)
    "},{"location":"tools/sprite_compiler/overview/#next-steps","title":"Next Steps","text":"
    • Installation Guide - Set up the compiler
    • Usage Guide - Learn how to use it
    • Advanced Features - Explore advanced options
    "},{"location":"tools/sprite_compiler/overview/#see-also","title":"See Also","text":"
    • Manual - Sprites and Animation - Using sprites in games
    • Code Examples - Sprites - Sprite usage examples
    • Available Tools - All PixelRoot32 tools
    "},{"location":"tools/sprite_compiler/usage_guide/","title":"Sprite Compiler Usage Guide","text":"

    Complete guide to using the PixelRoot32 Sprite Compiler for converting images to sprite data.

    "},{"location":"tools/sprite_compiler/usage_guide/#basic-usage","title":"Basic Usage","text":""},{"location":"tools/sprite_compiler/usage_guide/#launching-the-gui","title":"Launching the GUI","text":"

    The easiest way to use the compiler is via the Graphical User Interface (GUI).

    python main.py\n

    This will open the application where you can interactively load images, configure the grid, and export sprites.

    "},{"location":"tools/sprite_compiler/usage_guide/#command-line-interface-cli","title":"Command Line Interface (CLI)","text":"

    For automation, you can use the CLI mode by passing arguments to the script.

    python main.py [input] [options]\n

    Required:

    • input: Input PNG image file
    • --grid WxH: Grid cell size (e.g., 16x16)
    • --sprite gx,gy,gw,gh: Sprite definition (can be repeated)

    Optional:

    • --prefix NAME: Prefix for generated arrays (e.g., PLAYER_JUM)
    • --out FILE: Output header file (default: sprites.h)
    • --offset X,Y: Initial offset in pixels (default: 0,0)
    • --mode MODE: Export mode (layered, 2bpp, 4bpp)
    "},{"location":"tools/sprite_compiler/usage_guide/#cli-examples","title":"CLI Examples","text":""},{"location":"tools/sprite_compiler/usage_guide/#simple-conversion","title":"Simple Conversion","text":"

    Convert a single 16x16 sprite located at the top-left corner:

    python main.py player.png --grid 16x16 --sprite 0,0,1,1 --out player.h\n
    "},{"location":"tools/sprite_compiler/usage_guide/#multiple-sprites","title":"Multiple Sprites","text":"

    Convert multiple sprites from a single sheet:

    python main.py sheet.png --grid 16x16 \\\n  --sprite 0,0,1,1 \\\n  --sprite 1,0,1,1 \\\n  --sprite 2,0,1,1 \\\n  --out animations.h\n
    "},{"location":"tools/sprite_compiler/usage_guide/#export-modes","title":"Export Modes","text":"

    Layered (Default): Generates multiple uint16_t arrays, one for each color layer. Best for standard PixelRoot32 rendering.

    python main.py icon.png --grid 16x16 --sprite 0,0,1,1 --mode layered\n

    Packed 2bpp: Generates a single array with 2 bits per pixel (4 colors max).

    python main.py icon.png --grid 16x16 --sprite 0,0,1,1 --mode 2bpp\n
    "},{"location":"tools/sprite_compiler/usage_guide/#step-by-step-examples","title":"Step-by-Step Examples","text":""},{"location":"tools/sprite_compiler/usage_guide/#example-1-simple-player-sprite","title":"Example 1: Simple Player Sprite","text":"

    Step 1: Create Image

    • Create an 8x8 pixel PNG image
    • Use black and white colors
    • Save as player.png

    Step 2: Compile

    python main.py player.png --grid 8x8 --sprite 0,0,1,1 --prefix PLAYER --out player_sprite.h\n

    Step 3: Use in Code

    #include \"player_sprite.h\"\n\nvoid draw() {\n    // PLAYER_SPRITE_0_LAYER_0 is generated automatically\n    renderer.drawSprite(PLAYER_SPRITE_0_LAYER_0, 100, 100, Color::White);\n}\n
    "},{"location":"tools/sprite_compiler/usage_guide/#example-2-multiple-animation-frames","title":"Example 2: Multiple Animation Frames","text":"

    Step 1: Prepare Images

    • Create frames: walk_0.png, walk_1.png, walk_2.png
    • All same size (e.g., 16x16)

    Step 2: Batch Compile

    pr32-sprite-compiler --batch walk_*.png --output-dir animations/ --prefix WALK_\n

    Step 3: Use in Animation

    #include \"animations/walk_0.h\"\n#include \"animations/walk_1.h\"\n#include \"animations/walk_2.h\"\n\nconst Sprite* WALK_FRAMES[] = {\n    &WALK_0_SPRITE,\n    &WALK_1_SPRITE,\n    &WALK_2_SPRITE\n};\n
    "},{"location":"tools/sprite_compiler/usage_guide/#example-3-sprite-sheet","title":"Example 3: Sprite Sheet","text":"

    Step 1: Create Sprite Sheet

    • Create a 64x64 image with 4x4 grid of 16x16 sprites
    • Save as characters.png

    Step 2: Split Sheet

    pr32-sprite-compiler characters.png characters.h --sheet 16x16 --count 16\n

    Step 3: Use Individual Sprites

    #include \"characters.h\"\n\n// Sprites named CHARACTER_0, CHARACTER_1, etc.\nrenderer.drawSprite(CHARACTER_0, 50, 50, Color::White);\nrenderer.drawSprite(CHARACTER_1, 70, 50, Color::White);\n
    "},{"location":"tools/sprite_compiler/usage_guide/#batch-processing","title":"Batch Processing","text":""},{"location":"tools/sprite_compiler/usage_guide/#process-multiple-files","title":"Process Multiple Files","text":"

    Process all PNG files in a directory:

    pr32-sprite-compiler --batch sprites/*.png --output-dir generated/\n
    "},{"location":"tools/sprite_compiler/usage_guide/#with-options","title":"With Options","text":"

    Apply options to all files:

    pr32-sprite-compiler --batch assets/*.png \\\n    --output-dir src/sprites/ \\\n    --format 1bpp \\\n    --prefix SPRITE_\n
    "},{"location":"tools/sprite_compiler/usage_guide/#recursive-processing","title":"Recursive Processing","text":"

    Process subdirectories:

    pr32-sprite-compiler --batch assets/**/*.png --output-dir generated/\n
    "},{"location":"tools/sprite_compiler/usage_guide/#sprite-sheets","title":"Sprite Sheets","text":""},{"location":"tools/sprite_compiler/usage_guide/#automatic-splitting","title":"Automatic Splitting","text":"

    Split a sprite sheet into individual sprites:

    pr32-sprite-compiler sheet.png output.h --sheet 8x8 --count 16\n

    Parameters:

    • --sheet WxH: Tile size (width x height)
    • --count N: Number of sprites in sheet
    "},{"location":"tools/sprite_compiler/usage_guide/#grid-layout","title":"Grid Layout","text":"

    Specify grid dimensions:

    pr32-sprite-compiler sheet.png output.h \\\n    --sheet 16x16 \\\n    --grid 4x4 \\\n    --count 16\n

    Parameters:

    • --grid WxH: Grid dimensions (columns x rows)
    "},{"location":"tools/sprite_compiler/usage_guide/#custom-naming","title":"Custom Naming","text":"

    Name sprites with index:

    pr32-sprite-compiler sheet.png output.h \\\n    --sheet 8x8 \\\n    --count 8 \\\n    --prefix CHARACTER_ \\\n    --indexed\n

    Generates: CHARACTER_0, CHARACTER_1, etc.

    "},{"location":"tools/sprite_compiler/usage_guide/#custom-palettes","title":"Custom Palettes","text":""},{"location":"tools/sprite_compiler/usage_guide/#using-palette-file","title":"Using Palette File","text":"

    Convert with custom color palette:

    pr32-sprite-compiler sprite.png output.h --palette palette.json\n

    Palette JSON format:

    {\n  \"colors\": [\n    {\"r\": 0, \"g\": 0, \"b\": 0, \"name\": \"black\"},\n    {\"r\": 255, \"g\": 255, \"b\": 255, \"name\": \"white\"}\n  ]\n}\n
    "},{"location":"tools/sprite_compiler/usage_guide/#built-in-palettes","title":"Built-in Palettes","text":"

    Use predefined palettes:

    pr32-sprite-compiler sprite.png output.h --palette nes\npr32-sprite-compiler sprite.png output.h --palette gb\npr32-sprite-compiler sprite.png output.h --palette pico8\n
    "},{"location":"tools/sprite_compiler/usage_guide/#advanced-options","title":"Advanced Options","text":""},{"location":"tools/sprite_compiler/usage_guide/#threshold-for-grayscale","title":"Threshold for Grayscale","text":"

    Set threshold for black/white conversion:

    pr32-sprite-compiler sprite.png output.h --threshold 128\n

    Values: 0-255 (default: 127)

    "},{"location":"tools/sprite_compiler/usage_guide/#dithering","title":"Dithering","text":"

    Enable dithering for better gradients:

    pr32-sprite-compiler sprite.png output.h --dither\n
    "},{"location":"tools/sprite_compiler/usage_guide/#alignment","title":"Alignment","text":"

    Control output alignment:

    pr32-sprite-compiler sprite.png output.h --align 4\n
    "},{"location":"tools/sprite_compiler/usage_guide/#endianness","title":"Endianness","text":"

    Specify byte order:

    pr32-sprite-compiler sprite.png output.h --endian little\npr32-sprite-compiler sprite.png output.h --endian big\n
    "},{"location":"tools/sprite_compiler/usage_guide/#output-customization","title":"Output Customization","text":""},{"location":"tools/sprite_compiler/usage_guide/#namespace","title":"Namespace","text":"

    Wrap output in namespace:

    pr32-sprite-compiler sprite.png output.h --namespace MyGame\n
    "},{"location":"tools/sprite_compiler/usage_guide/#header-guard","title":"Header Guard","text":"

    Custom header guard:

    pr32-sprite-compiler sprite.png output.h --guard MY_SPRITE_H\n
    "},{"location":"tools/sprite_compiler/usage_guide/#include-paths","title":"Include Paths","text":"

    Custom include paths:

    pr32-sprite-compiler sprite.png output.h \\\n    --include \"<graphics/Sprite.h>\" \\\n    --include \"<stdint.h>\"\n
    "},{"location":"tools/sprite_compiler/usage_guide/#integration-with-build-systems","title":"Integration with Build Systems","text":""},{"location":"tools/sprite_compiler/usage_guide/#platformio","title":"PlatformIO","text":"

    Add to platformio.ini:

    [env:esp32dev]\nextra_scripts = \n    pre:scripts/compile_sprites.py\n

    compile_sprites.py:

    Import(\"env\")\nimport subprocess\n\nsubprocess.run([\n    \"pr32-sprite-compiler\",\n    \"--batch\", \"assets/sprites/*.png\",\n    \"--output-dir\", \"src/sprites/\"\n])\n
    "},{"location":"tools/sprite_compiler/usage_guide/#makefile","title":"Makefile","text":"
    SPRITES = $(wildcard assets/sprites/*.png)\nSPRITE_HEADERS = $(SPRITES:assets/sprites/%.png=src/sprites/%.h)\n\nsrc/sprites/%.h: assets/sprites/%.png\n pr32-sprite-compiler $< $@ --name $(shell basename $< .png | tr '[:lower:]' '[:upper:]')_SPRITE\n\nsprites: $(SPRITE_HEADERS)\n
    "},{"location":"tools/sprite_compiler/usage_guide/#cmake","title":"CMake","text":"
    file(GLOB SPRITE_FILES \"assets/sprites/*.png\")\n\nforeach(SPRITE ${SPRITE_FILES})\n    get_filename_component(SPRITE_NAME ${SPRITE} NAME_WE)\n    add_custom_command(\n        OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/sprites/${SPRITE_NAME}.h\n        COMMAND pr32-sprite-compiler\n        ARGS ${SPRITE} ${CMAKE_CURRENT_SOURCE_DIR}/src/sprites/${SPRITE_NAME}.h\n        DEPENDS ${SPRITE}\n    )\nendforeach()\n
    "},{"location":"tools/sprite_compiler/usage_guide/#gui-usage-if-available","title":"GUI Usage (If Available)","text":""},{"location":"tools/sprite_compiler/usage_guide/#opening-gui","title":"Opening GUI","text":"
    pr32-sprite-compiler --gui\n

    Or launch the GUI application directly.

    "},{"location":"tools/sprite_compiler/usage_guide/#gui-workflow","title":"GUI Workflow","text":"
    1. Drag and drop images into the window
    2. Preview sprite data in real-time
    3. Adjust settings visually (format, threshold, etc.)
    4. Export to header files
    5. Batch process multiple files
    "},{"location":"tools/sprite_compiler/usage_guide/#gui-features","title":"GUI Features","text":"
    • Visual preview of sprite conversion
    • Real-time threshold adjustment
    • Palette selection
    • Batch processing interface
    • Export options
    "},{"location":"tools/sprite_compiler/usage_guide/#best-practices","title":"Best Practices","text":""},{"location":"tools/sprite_compiler/usage_guide/#image-preparation","title":"Image Preparation","text":"
    • Use indexed color PNG for best results
    • Keep sprites small (8x8, 16x16, 32x32)
    • Use black and white for 1bpp
    • Limit colors for 2bpp/4bpp formats
    "},{"location":"tools/sprite_compiler/usage_guide/#file-organization","title":"File Organization","text":"
    project/\n\u251c\u2500\u2500 assets/\n\u2502   \u2514\u2500\u2500 sprites/\n\u2502       \u251c\u2500\u2500 player.png\n\u2502       \u251c\u2500\u2500 enemy.png\n\u2502       \u2514\u2500\u2500 items.png\n\u251c\u2500\u2500 src/\n\u2502   \u2514\u2500\u2500 sprites/          # Generated headers\n\u2502       \u251c\u2500\u2500 player.h\n\u2502       \u251c\u2500\u2500 enemy.h\n\u2502       \u2514\u2500\u2500 items.h\n\u2514\u2500\u2500 platformio.ini\n
    "},{"location":"tools/sprite_compiler/usage_guide/#naming-conventions","title":"Naming Conventions","text":"
    • Use descriptive names: player_walk_0.png \u2192 PLAYER_WALK_0_SPRITE
    • Be consistent: All caps for sprite names
    • Use prefixes: ENEMY_, PLAYER_, ITEM_
    "},{"location":"tools/sprite_compiler/usage_guide/#version-control","title":"Version Control","text":"
    • Commit generated headers (they're part of the build)
    • Or add to .gitignore and regenerate on build
    • Keep source images in version control
    "},{"location":"tools/sprite_compiler/usage_guide/#troubleshooting","title":"Troubleshooting","text":""},{"location":"tools/sprite_compiler/usage_guide/#common-issues","title":"Common Issues","text":"

    \"Image too large\":

    • Sprites must be \u2264 16 pixels wide for 1bpp
    • Resize image or split into multiple sprites

    \"Colors not converting correctly\":

    • Use indexed color PNG
    • For 1bpp: Use only black and white
    • For 2bpp: Use exactly 4 colors
    • For 4bpp: Use up to 16 colors

    \"Output file not found\":

    • Check write permissions
    • Verify output directory exists
    • Use absolute paths if needed

    \"Invalid format\":

    • Ensure input is PNG format
    • Check file is not corrupted
    • Try re-saving image in image editor
    "},{"location":"tools/sprite_compiler/usage_guide/#getting-help","title":"Getting Help","text":"
    pr32-sprite-compiler --help\n

    Shows all available options and usage.

    "},{"location":"tools/sprite_compiler/usage_guide/#next-steps","title":"Next Steps","text":"
    • Advanced Features - Explore advanced options
    • Overview - Learn more about the compiler
    • Manual - Sprites - Using sprites in games
    "},{"location":"tools/sprite_compiler/usage_guide/#see-also","title":"See Also","text":"
    • Code Examples - Sprites - Sprite usage examples
    • Troubleshooting - Common issues and solutions
    "},{"location":"tools/tilemap_editor/installation/","title":"Installation Guide","text":"

    The PixelRoot32 Tilemap Editor can be run directly from source or as a standalone executable on Windows.

    "},{"location":"tools/tilemap_editor/installation/#1-requirements","title":"1. Requirements","text":"
    • Python 3.13+ (if running from source).
    • Windows 10/11 (recommended).
    "},{"location":"tools/tilemap_editor/installation/#2-install-from-source","title":"2. Install from Source","text":""},{"location":"tools/tilemap_editor/installation/#21-clone-the-repository","title":"2.1 Clone the Repository","text":"
    git clone https://github.com/Gperez88/PixelRoot32-Tilemap-Editor.git\ncd PixelRoot32-Tilemap-Editor\n
    "},{"location":"tools/tilemap_editor/installation/#22-install-dependencies","title":"2.2 Install Dependencies","text":"

    The editor uses several Python libraries for the GUI and image processing:

    pip install ttkbootstrap pillow jinja2\n
    "},{"location":"tools/tilemap_editor/installation/#23-run-the-editor","title":"2.3 Run the Editor","text":"
    python main.py\n
    "},{"location":"tools/tilemap_editor/installation/#3-standalone-executable-windows","title":"3. Standalone Executable (Windows)","text":"

    For a more convenient experience, you can use the pre-compiled version:

    1. Go to the Releases section of the repository.
    2. Download the latest PixelRoot32-Editor-win64.zip.
    3. Extract the contents to a folder.
    4. Run PixelRoot32-Editor.exe.

    Note: No Python installation is required to run the standalone executable.

    "},{"location":"tools/tilemap_editor/installation/#4-building-your-own-executable","title":"4. Building your own Executable","text":"

    If you want to package the editor yourself:

    1. Install PyInstaller:
    pip install pyinstaller\n
    1. Run the build command using the provided .spec file:
    pyinstaller pixelroot32_editor.spec\n
    1. The executable will be available in the dist/ folder.
    "},{"location":"tools/tilemap_editor/overview/","title":"Tilemap Editor Overview","text":"

    The PixelRoot32 Tilemap Editor is a powerful visual tool designed to create complex multi-layered tile-based maps for the PixelRoot32 engine. It simplifies the process of designing game environments, managing tilesets, and exporting optimized C++ code.

    "},{"location":"tools/tilemap_editor/overview/#what-it-does","title":"What It Does","text":"

    The Tilemap Editor allows you to:

    • Visual Design: Paint tiles directly onto a canvas with layers and transparency.
    • Tileset Management: Import PNG images as tilesets and select single or multiple tiles.
    • Multi-Layer Support: Organize your map into up to 8 layers for parallax effects or depth.
    • Optimized Export: Generate C++ header and source files compatible with the PixelRoot32 renderer.
    • BPP Support: Export maps in 1bpp, 2bpp, or 4bpp formats to balance memory usage and color depth.
    "},{"location":"tools/tilemap_editor/overview/#key-features","title":"Key Features","text":""},{"location":"tools/tilemap_editor/overview/#visual-editing-tools","title":"\u2705 Visual Editing Tools","text":"
    • Brush: Paint individual tiles or patterns.
    • Eraser: Remove tiles from the active layer.
    • Rectangle Fill: Quickly fill areas with a specific tile.
    • Pipette: Pick an existing tile from the canvas.
    "},{"location":"tools/tilemap_editor/overview/#multi-layer-system","title":"\u2705 Multi-Layer System","text":"
    • Visibility Toggle: Hide/show layers to focus on specific parts of the map.
    • Opacity Control: Adjust layer transparency for complex blending effects.
    • Layer Reordering: Change the render order of your tilemaps.
    "},{"location":"tools/tilemap_editor/overview/#tileset-selector","title":"\u2705 Tileset Selector","text":"
    • Smart Selection: Drag and select a rectangular area of tiles.
    • Multiple Tilesets: Support for multiple tilesets per project (planned).
    • Auto-import: Automatically detects tile size from the imported image.
    "},{"location":"tools/tilemap_editor/overview/#engine-integration","title":"\u2705 Engine Integration","text":"
    • Workspace Selection: Link the editor to your PixelRoot32 projects directory.
    • Direct Export: Files are generated with the correct namespaces and structures for immediate use.
    • BPP Compatibility: Ensures exported data matches the engine's expected format for 1bpp, 2bpp, and 4bpp.
    "},{"location":"tools/tilemap_editor/overview/#data-formats","title":"Data Formats","text":""},{"location":"tools/tilemap_editor/overview/#project-file-pr32scene","title":"Project File (.pr32scene)","text":"

    The editor uses a custom JSON-based format to save your project state, including:

    • Tileset metadata (path, tile size, spacing).
    • Layer data (tile indices, width, height, position).
    • Project settings (BPP, namespace).
    "},{"location":"tools/tilemap_editor/overview/#exported-c","title":"Exported C++","text":"

    The editor generates .h and .cpp files containing:

    • Tilemap Data: Packed arrays of tile indices.
    • Tilemap Structures: pixelroot32::graphics::TileMap (or TileMap2bpp/TileMap4bpp) definitions.
    • Scene Headers: Convenient entry points for loading entire maps into your game.
    "},{"location":"tools/tilemap_editor/usage_guide/","title":"Usage Guide","text":"

    This guide covers the basic workflow for creating and exporting a tilemap using the PixelRoot32 Tilemap Editor.

    "},{"location":"tools/tilemap_editor/usage_guide/#1-creating-a-new-project","title":"1. Creating a New Project","text":"
    1. Launch the editor.
    2. Go to File > New Project.
    3. Enter the project name and select the base Color Depth (BPP):
    4. 1bpp: Monochrome (2 colors).
    5. 2bpp: 4 colors.
    6. 4bpp: 16 colors.
    7. Set the Tile Size (e.g., 8x8, 16x16).
    "},{"location":"tools/tilemap_editor/usage_guide/#2-importing-a-tileset","title":"2. Importing a Tileset","text":"
    1. In the Tileset panel, click on Load Tileset.
    2. Select a PNG image containing your tiles.
    3. The image will be sliced into tiles based on the tile size set in the project.
    "},{"location":"tools/tilemap_editor/usage_guide/#3-painting-tiles","title":"3. Painting Tiles","text":"
    1. Select a tile (or a range of tiles) from the Tileset panel.
    2. Select the Brush tool (Shortcut: B).
    3. Click and drag on the canvas to paint.
    4. Use the Layers panel to switch between different layers.
    "},{"location":"tools/tilemap_editor/usage_guide/#4-selection-and-transformations","title":"4. Selection and Transformations","text":"
    • Single Selection: Click on a tile in the tileset.
    • Area Selection: Click and drag in the tileset to select a rectangular block of tiles.
    • Pipette: Press P and click on the canvas to pick the tile under the cursor.
    "},{"location":"tools/tilemap_editor/usage_guide/#5-exporting-to-c","title":"5. Exporting to C++","text":"
    1. Ensure your Workspace Path is correctly set in Project Settings. This should point to your PixelRoot32 project's src or include directory.
    2. Click on File > Export.
    3. The editor will generate:
    4. <ProjectName>_data.h and .cpp: Containing the raw tile data and structures.
    5. <ProjectName>_scene.h: A high-level header to include in your game.
    "},{"location":"tools/tilemap_editor/usage_guide/#6-keyboard-shortcuts","title":"6. Keyboard Shortcuts","text":"Shortcut Action B Brush Tool E Eraser Tool R Rectangle Fill Tool P Pipette Tool Space + Drag Pan Canvas Mouse Wheel Zoom In/Out Ctrl + N New Project Ctrl + S Save Project Ctrl + E Export Project Esc Close floating panels"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"PixelRoot32 Documentation","text":"

    PixelRoot32 is a lightweight 2D game engine designed for ESP32 and native desktop targets. This site provides official, versioned documentation with clear guides, conceptual explanations, API references, and complete examples to help you build games efficiently.

    "},{"location":"#quick-links","title":"Quick Links","text":"
    • What is PixelRoot32? - Start here to understand the engine
    • Your First Project - Get up and running quickly
    • Fundamental Concepts - Learn the core concepts
    • Manual - Complete user guide
    • API Reference - Complete API documentation
    • Examples - Complete game examples
    • Tools - Available tools
    • FAQ - FAQ and troubleshooting
    "},{"location":"#getting-started","title":"Getting Started","text":"

    New to PixelRoot32? Follow this learning path:

    1. What is PixelRoot32? - Understand what the engine is and what it can do
    2. Why PixelRoot32? - Learn the advantages and use cases
    3. Fundamental Concepts - Learn the core architecture concepts
    4. Your First Project - Create and run your first project
    "},{"location":"#about-this-documentation","title":"About This Documentation","text":"
    • Professional technical English across all pages
    • Search-enabled, mobile-friendly UI
    • Versioned with mike (stable/dev/experimental)
    • Cross-linked concepts, API, and examples
    • Progressive learning path from basics to advanced topics
    "},{"location":"api_reference/audio/audio_config/","title":"AudioConfig","text":"

    Configuration for the Audio subsystem.

    "},{"location":"api_reference/audio/audio_config/#description","title":"Description","text":"

    AudioConfig is a simple struct that holds configuration settings for the audio system, including the audio backend and sample rate. It is passed to AudioEngine during construction.

    "},{"location":"api_reference/audio/audio_config/#namespace","title":"Namespace","text":"
    namespace pixelroot32::audio {\n    struct AudioConfig {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/audio/audio_config/#structure","title":"Structure","text":""},{"location":"api_reference/audio/audio_config/#audiobackend-backend","title":"AudioBackend* backend","text":"

    Pointer to the platform-specific audio backend implementation.

    Type: AudioBackend*

    Access: Read-write

    Default: nullptr

    Notes: - Must be set to a valid backend instance - Backend is platform-specific: - ESP32: ESP32_DAC_AudioBackend or ESP32_I2S_AudioBackend - Native: SDL2_AudioBackend - Backend manages the actual audio hardware/API

    Example:

    #ifdef PLATFORM_ESP32\n    pixelroot32::drivers::esp32::ESP32_DAC_AudioBackend dacBackend;\n    audioConfig.backend = &dacBackend;\n#elif PLATFORM_NATIVE\n    pixelroot32::drivers::native::SDL2_AudioBackend sdlBackend;\n    audioConfig.backend = &sdlBackend;\n#endif\n

    "},{"location":"api_reference/audio/audio_config/#int-samplerate","title":"int sampleRate","text":"

    Desired sample rate in Hz.

    Type: int

    Access: Read-write

    Default: 22050

    Notes: - Common values: 11025, 22050, 44100 - Lower rates use less CPU and memory (better for ESP32) - Higher rates provide better quality - Must match backend capabilities

    Example:

    audioConfig.sampleRate = 11025;  // Lower quality, less CPU (ESP32)\naudioConfig.sampleRate = 22050;  // Balanced (default)\naudioConfig.sampleRate = 44100; // Higher quality (Native)\n

    "},{"location":"api_reference/audio/audio_config/#constructors","title":"Constructors","text":""},{"location":"api_reference/audio/audio_config/#audioconfigaudiobackend-backend-nullptr-int-samplerate-22050","title":"AudioConfig(AudioBackend* backend = nullptr, int sampleRate = 22050)","text":"

    Default constructor.

    Parameters: - backend (AudioBackend*, optional): Pointer to the audio backend implementation. Default: nullptr - sampleRate (int, optional): Desired sample rate in Hz. Default: 22050

    Example:

    // Default construction\npixelroot32::audio::AudioConfig audioConfig;\n\n// With backend\npixelroot32::drivers::esp32::ESP32_DAC_AudioBackend dacBackend;\npixelroot32::audio::AudioConfig audioConfig(&dacBackend, 11025);\n

    "},{"location":"api_reference/audio/audio_config/#usage-example","title":"Usage Example","text":""},{"location":"api_reference/audio/audio_config/#esp32-with-dac-backend","title":"ESP32 with DAC Backend","text":"
    #ifdef PLATFORM_ESP32\n#include \"drivers/esp32/ESP32_DAC_AudioBackend.h\"\n\npixelroot32::drivers::esp32::ESP32_DAC_AudioBackend dacBackend;\n\npixelroot32::audio::AudioConfig audioConfig;\naudioConfig.backend = &dacBackend;\naudioConfig.sampleRate = 11025;  // Lower rate for ESP32\n\npixelroot32::audio::AudioEngine audioEngine(audioConfig);\naudioEngine.init();\n#endif\n
    "},{"location":"api_reference/audio/audio_config/#esp32-with-i2s-backend","title":"ESP32 with I2S Backend","text":"
    #ifdef PLATFORM_ESP32\n#include \"drivers/esp32/ESP32_I2S_AudioBackend.h\"\n\npixelroot32::drivers::esp32::ESP32_I2S_AudioBackend i2sBackend;\n\npixelroot32::audio::AudioConfig audioConfig;\naudioConfig.backend = &i2sBackend;\naudioConfig.sampleRate = 22050;  // Higher quality with I2S\n\npixelroot32::audio::AudioEngine audioEngine(audioConfig);\naudioEngine.init();\n#endif\n
    "},{"location":"api_reference/audio/audio_config/#native-with-sdl2-backend","title":"Native with SDL2 Backend","text":"
    #ifdef PLATFORM_NATIVE\n#include \"drivers/native/SDL2_AudioBackend.h\"\n\npixelroot32::drivers::native::SDL2_AudioBackend sdlBackend;\n\npixelroot32::audio::AudioConfig audioConfig;\naudioConfig.backend = &sdlBackend;\naudioConfig.sampleRate = 44100;  // High quality for PC\n\npixelroot32::audio::AudioEngine audioEngine(audioConfig);\naudioEngine.init();\n#endif\n
    "},{"location":"api_reference/audio/audio_config/#complete-engine-setup","title":"Complete Engine Setup","text":"
    #include \"core/Engine.h\"\n#include \"graphics/DisplayConfig.h\"\n#include \"input/InputConfig.h\"\n#include \"audio/AudioConfig.h\"\n\nvoid setup() {\n    // Display config\n    pixelroot32::graphics::DisplayConfig displayConfig;\n    displayConfig.width = 128;\n    displayConfig.height = 128;\n\n    // Input config\n    pixelroot32::input::InputConfig inputConfig;\n    // ... configure input\n\n    // Audio config\n    #ifdef PLATFORM_ESP32\n        pixelroot32::drivers::esp32::ESP32_DAC_AudioBackend dacBackend;\n        pixelroot32::audio::AudioConfig audioConfig(&dacBackend, 11025);\n    #elif PLATFORM_NATIVE\n        pixelroot32::drivers::native::SDL2_AudioBackend sdlBackend;\n        pixelroot32::audio::AudioConfig audioConfig(&sdlBackend, 44100);\n    #endif\n\n    // Create engine with all configs\n    pixelroot32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n    engine.init();\n    engine.run();\n}\n
    "},{"location":"api_reference/audio/audio_config/#platform-specific-considerations","title":"Platform-Specific Considerations","text":""},{"location":"api_reference/audio/audio_config/#esp32-dac-backend","title":"ESP32 DAC Backend","text":"
    • Sample rate: 11025 Hz recommended (lower CPU usage)
    • Quality: Lower quality, but simple setup
    • Pin: Uses GPIO 25 or 26
    • Hardware: Requires simple amplifier circuit
    "},{"location":"api_reference/audio/audio_config/#esp32-i2s-backend","title":"ESP32 I2S Backend","text":"
    • Sample rate: 22050 Hz recommended
    • Quality: Higher quality than DAC
    • Pins: Requires I2S pins (BCLK, LRCK, DOUT)
    • Hardware: Requires external I2S DAC
    "},{"location":"api_reference/audio/audio_config/#native-sdl2-backend","title":"Native SDL2 Backend","text":"
    • Sample rate: 44100 Hz typical
    • Quality: High quality
    • Setup: Requires SDL2 library installed
    • Platforms: Windows, Linux, macOS
    "},{"location":"api_reference/audio/audio_config/#performance-considerations","title":"Performance Considerations","text":"
    • Sample rate: Lower rates use less CPU and memory
    • Backend choice: DAC is simpler but lower quality than I2S
    • Buffer size: Configured in backend, affects latency vs stability
    "},{"location":"api_reference/audio/audio_config/#see-also","title":"See Also","text":"
    • AudioEngine - Audio playback engine
    • Manual - Audio
    • Manual - Platforms and Drivers
    • API Overview
    "},{"location":"api_reference/audio/audio_engine/","title":"AudioEngine","text":"

    Core class for the NES-like audio subsystem.

    "},{"location":"api_reference/audio/audio_engine/#description","title":"Description","text":"

    AudioEngine manages the audio channels (Pulse, Triangle, Noise), mixes their output, and provides the audio stream to the backend. It implements a NES-like audio system with 4 fixed channels: 2 Pulse channels, 1 Triangle channel, and 1 Noise channel.

    The engine is event-driven: you trigger sound effects via playEvent(), and the engine automatically manages channel allocation and playback.

    "},{"location":"api_reference/audio/audio_engine/#namespace","title":"Namespace","text":"
    namespace pixelroot32::audio {\n    class AudioEngine {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/audio/audio_engine/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Engine (manages audio engine instance)
    "},{"location":"api_reference/audio/audio_engine/#constructors","title":"Constructors","text":""},{"location":"api_reference/audio/audio_engine/#audioengineconst-audioconfig-config","title":"AudioEngine(const AudioConfig& config)","text":"

    Constructs the AudioEngine with the given configuration.

    Parameters: - config (const AudioConfig&): Configuration struct containing the backend and parameters (sample rate, etc.)

    Example:

    #include \"audio/AudioEngine.h\"\n#include \"audio/AudioConfig.h\"\n\npixelroot32::audio::AudioConfig audioConfig;\naudioConfig.backend = &audioBackend;  // Platform-specific backend\naudioConfig.sampleRate = 22050;       // 22.05 kHz for retro feel\n\npixelroot32::audio::AudioEngine audioEngine(audioConfig);\naudioEngine.init();\n

    "},{"location":"api_reference/audio/audio_engine/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/audio/audio_engine/#void-init","title":"void init()","text":"

    Initializes the audio subsystem and the backend.

    Returns: - void

    Notes: - Must be called after construction and before use - Initializes the platform-specific audio backend - Safe to call multiple times (idempotent) - Typically called automatically by Engine::init()

    Example:

    AudioEngine audioEngine(audioConfig);\naudioEngine.init();  // Initialize before use\n

    "},{"location":"api_reference/audio/audio_engine/#void-updateunsigned-long-deltatime","title":"void update(unsigned long deltaTime)","text":"

    Updates the audio state based on game time.

    Parameters: - deltaTime (unsigned long): Time elapsed since last frame in milliseconds

    Returns: - void

    Notes: - Should be called from the main game loop (typically via Engine::update()) - Updates channel lifetimes and durations - Automatically stops channels when their duration expires - Must be called every frame for proper audio timing

    Example:

    void update(unsigned long deltaTime) override {\n    // Update audio (called automatically by Engine)\n    engine.getAudioEngine().update(deltaTime);\n\n    // Your game logic...\n}\n

    "},{"location":"api_reference/audio/audio_engine/#void-generatesamplesint16_t-stream-int-length","title":"void generateSamples(int16_t* stream, int length)","text":"

    Fills the provided buffer with mixed audio samples.

    Parameters: - stream (int16_t*): Pointer to the buffer to fill - length (int): Number of samples to generate

    Returns: - void

    Notes: - This method is typically called by the AudioBackend from an audio callback or task - Not usually called directly by game code - Generates 16-bit signed integer PCM samples - Mixes all active channels into a mono stream

    Advanced Usage:

    // Typically not called directly, but if implementing custom backend:\nint16_t buffer[512];\naudioEngine.generateSamples(buffer, 512);\n

    "},{"location":"api_reference/audio/audio_engine/#void-playeventconst-audioevent-event","title":"void playEvent(const AudioEvent& event)","text":"

    Triggers a one-shot sound effect.

    Parameters: - event (const AudioEvent&): The audio event to play

    Returns: - void

    Notes: - Automatically finds an available channel of the correct type - If no channel is available, the event may be dropped (no error) - Events are fire-and-forget (no need to track playback) - Use for sound effects, not background music

    Example:

    // Play a jump sound\npixelroot32::audio::AudioEvent jumpSound{};\njumpSound.type = pixelroot32::audio::WaveType::PULSE;\njumpSound.frequency = 800.0f;\njumpSound.duration = 0.1f;\njumpSound.volume = 0.7f;\njumpSound.duty = 0.5f;\n\nauto& audio = engine.getAudioEngine();\naudio.playEvent(jumpSound);\n\n// Play an explosion sound\npixelroot32::audio::AudioEvent explosion{};\nexplosion.type = pixelroot32::audio::WaveType::NOISE;\nexplosion.frequency = 1000.0f;\nexplosion.duration = 0.3f;\nexplosion.volume = 0.9f;\n\naudio.playEvent(explosion);\n

    "},{"location":"api_reference/audio/audio_engine/#void-setmastervolumefloat-volume","title":"void setMasterVolume(float volume)","text":"

    Sets the master volume for all audio output.

    Parameters: - volume (float): Volume level (0.0 = silent, 1.0 = full volume)

    Returns: - void

    Notes: - Affects all channels and events - Clamped to [0.0, 1.0] range - Use for volume control menus or mute functionality

    Example:

    auto& audio = engine.getAudioEngine();\naudio.setMasterVolume(0.5f);  // 50% volume\naudio.setMasterVolume(0.0f);  // Mute\naudio.setMasterVolume(1.0f);  // Full volume\n

    "},{"location":"api_reference/audio/audio_engine/#float-getmastervolume-const","title":"float getMasterVolume() const","text":"

    Gets the current master volume.

    Returns: - float: Current master volume (0.0 to 1.0)

    Example:

    float currentVolume = audioEngine.getMasterVolume();\n

    "},{"location":"api_reference/audio/audio_engine/#audio-channels","title":"Audio Channels","text":"

    The engine manages 4 fixed channels:

    1. Channel 0: Pulse wave
    2. Channel 1: Pulse wave
    3. Channel 2: Triangle wave
    4. Channel 3: Noise wave

    Notes: - Channels are automatically allocated when playing events - If all channels of a type are busy, new events may be dropped - Background music typically uses one channel (via MusicPlayer)

    "},{"location":"api_reference/audio/audio_engine/#usage-example","title":"Usage Example","text":"
    #include \"audio/AudioEngine.h\"\n#include \"audio/AudioConfig.h\"\n\nclass MyScene : public pixelroot32::core::Scene {\nprivate:\n    void playJumpSound() {\n        auto& audio = engine.getAudioEngine();\n\n        pixelroot32::audio::AudioEvent sound{};\n        sound.type = pixelroot32::audio::WaveType::PULSE;\n        sound.frequency = 800.0f;\n        sound.duration = 0.1f;\n        sound.volume = 0.7f;\n        sound.duty = 0.5f;\n\n        audio.playEvent(sound);\n    }\n\n    void playHitSound() {\n        auto& audio = engine.getAudioEngine();\n\n        pixelroot32::audio::AudioEvent sound{};\n        sound.type = pixelroot32::audio::WaveType::NOISE;\n        sound.frequency = 500.0f;\n        sound.duration = 0.05f;\n        sound.volume = 0.5f;\n\n        audio.playEvent(sound);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Audio is updated automatically by Engine\n        // Just play events when needed\n        if (playerJumped) {\n            playJumpSound();\n            playerJumped = false;\n        }\n    }\n};\n
    "},{"location":"api_reference/audio/audio_engine/#performance-considerations","title":"Performance Considerations","text":"
    • Channel limit: Only 4 channels total; plan sound effects accordingly
    • Event dropping: If all channels are busy, new events are silently dropped
    • Update frequency: update() must be called every frame for proper timing
    • Sample generation: generateSamples() is called by backend at audio rate (not game rate)
    "},{"location":"api_reference/audio/audio_engine/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Sample rate: Lower sample rates (11025 Hz) use less CPU and memory
    • Backend choice: DAC backend is simpler but lower quality than I2S
    • Buffer size: Larger buffers reduce underruns but increase latency
    • Channel management: Limit simultaneous sounds to avoid channel conflicts
    "},{"location":"api_reference/audio/audio_engine/#see-also","title":"See Also","text":"
    • AudioConfig - Audio configuration
    • AudioTypes - Audio data structures
    • MusicPlayer - Background music playback
    • Manual - Audio
    • API Overview
    "},{"location":"api_reference/audio/audio_types/","title":"Audio Types","text":"

    Data structures and types for the audio system.

    "},{"location":"api_reference/audio/audio_types/#description","title":"Description","text":"

    This document describes the data structures used by the audio system, including wave types, audio events, and channel state.

    "},{"location":"api_reference/audio/audio_types/#namespace","title":"Namespace","text":"
    namespace pixelroot32::audio {\n    // Types and structures\n}\n
    "},{"location":"api_reference/audio/audio_types/#wavetype-enum","title":"WaveType Enum","text":"

    Defines the types of waveforms available.

    Values: - WaveType::PULSE: Pulse wave (square wave with variable duty cycle) - WaveType::TRIANGLE: Triangle wave (smooth, melodic) - WaveType::NOISE: Noise wave (random, percussive)

    Example:

    pixelroot32::audio::WaveType wave = pixelroot32::audio::WaveType::PULSE;\n

    "},{"location":"api_reference/audio/audio_types/#audioevent-structure","title":"AudioEvent Structure","text":"

    A fire-and-forget sound event triggered by the game.

    Members: - WaveType type: Type of waveform to use - float frequency: Frequency in Hz - float duration: Duration in seconds - float volume: Volume level (0.0 to 1.0) - float duty: Duty cycle for pulse wave (0.0 to 1.0, pulse only)

    Example:

    pixelroot32::audio::AudioEvent jumpSound{};\njumpSound.type = pixelroot32::audio::WaveType::PULSE;\njumpSound.frequency = 800.0f;\njumpSound.duration = 0.1f;\njumpSound.volume = 0.7f;\njumpSound.duty = 0.5f;\n\nauto& audio = engine.getAudioEngine();\naudio.playEvent(jumpSound);\n

    "},{"location":"api_reference/audio/audio_types/#common-sound-effects","title":"Common Sound Effects","text":"

    Jump Sound:

    pixelroot32::audio::AudioEvent jump{};\njump.type = pixelroot32::audio::WaveType::PULSE;\njump.frequency = 800.0f;\njump.duration = 0.1f;\njump.volume = 0.7f;\njump.duty = 0.5f;\n

    Hit Sound:

    pixelroot32::audio::AudioEvent hit{};\nhit.type = pixelroot32::audio::WaveType::NOISE;\nhit.frequency = 500.0f;\nhit.duration = 0.05f;\nhit.volume = 0.5f;\n

    Collect Sound:

    pixelroot32::audio::AudioEvent collect{};\ncollect.type = pixelroot32::audio::WaveType::TRIANGLE;\ncollect.frequency = 1000.0f;\ncollect.duration = 0.15f;\ncollect.volume = 0.6f;\n

    Explosion:

    pixelroot32::audio::AudioEvent explosion{};\nexplosion.type = pixelroot32::audio::WaveType::NOISE;\nexplosion.frequency = 200.0f;\nexplosion.duration = 0.3f;\nexplosion.volume = 0.9f;\n

    "},{"location":"api_reference/audio/audio_types/#audiochannel-structure","title":"AudioChannel Structure","text":"

    Represents the internal state of a single audio channel.

    Members: - bool enabled: Whether the channel is active - WaveType type: Type of waveform - float frequency: Current frequency in Hz - float phase: Current phase (0.0 to 1.0) - float phaseIncrement: Pre-calculated phase increment - float volume: Current volume (0.0 to 1.0) - float targetVolume: Target volume for envelopes - float dutyCycle: Duty cycle for pulse wave (0.0 to 1.0) - uint16_t noiseRegister: LFSR state for noise generation - unsigned long durationMs: Total duration in milliseconds - unsigned long remainingMs: Remaining duration in milliseconds

    Methods: - void reset(): Resets the channel to inactive state

    Notes: - Internal structure, typically not accessed directly - Managed automatically by AudioEngine - 4 channels total: 2 Pulse, 1 Triangle, 1 Noise

    "},{"location":"api_reference/audio/audio_types/#frequency-reference","title":"Frequency Reference","text":"

    Common frequencies for musical notes (A4 = 440 Hz):

    • C4: 261.63 Hz
    • D4: 293.66 Hz
    • E4: 329.63 Hz
    • F4: 349.23 Hz
    • G4: 392.00 Hz
    • A4: 440.00 Hz
    • B4: 493.88 Hz
    • C5: 523.25 Hz

    Example:

    // Play a C note\npixelroot32::audio::AudioEvent note{};\nnote.type = pixelroot32::audio::WaveType::PULSE;\nnote.frequency = 261.63f;  // C4\nnote.duration = 0.5f;\nnote.volume = 0.8f;\nnote.duty = 0.5f;\n

    "},{"location":"api_reference/audio/audio_types/#duty-cycle-pulse-wave","title":"Duty Cycle (Pulse Wave)","text":"

    Duty cycle controls the shape of the pulse wave:

    • 0.125 (12.5%): Thin pulse (NES-like)
    • 0.25 (25%): Narrow pulse
    • 0.5 (50%): Square wave (most common)
    • 0.75 (75%): Wide pulse

    Example:

    // Thin pulse (NES style)\nevent.duty = 0.125f;\n\n// Square wave (standard)\nevent.duty = 0.5f;\n

    "},{"location":"api_reference/audio/audio_types/#usage-examples","title":"Usage Examples","text":""},{"location":"api_reference/audio/audio_types/#creating-sound-effect-library","title":"Creating Sound Effect Library","text":"
    namespace SoundEffects {\n    // Jump sound\n    inline pixelroot32::audio::AudioEvent jump() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::PULSE;\n        evt.frequency = 800.0f;\n        evt.duration = 0.1f;\n        evt.volume = 0.7f;\n        evt.duty = 0.5f;\n        return evt;\n    }\n\n    // Hit sound\n    inline pixelroot32::audio::AudioEvent hit() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::NOISE;\n        evt.frequency = 500.0f;\n        evt.duration = 0.05f;\n        evt.volume = 0.5f;\n        return evt;\n    }\n\n    // Collect sound\n    inline pixelroot32::audio::AudioEvent collect() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::TRIANGLE;\n        evt.frequency = 1000.0f;\n        evt.duration = 0.15f;\n        evt.volume = 0.6f;\n        return evt;\n    }\n}\n\n// Usage\nauto& audio = engine.getAudioEngine();\naudio.playEvent(SoundEffects::jump());\naudio.playEvent(SoundEffects::hit());\n
    "},{"location":"api_reference/audio/audio_types/#frequency-sweep-effect","title":"Frequency Sweep Effect","text":"
    void playSweepSound() {\n    auto& audio = engine.getAudioEngine();\n\n    // Create multiple events for sweep effect\n    for (int i = 0; i < 5; i++) {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::PULSE;\n        evt.frequency = 400.0f + (i * 200.0f);  // Sweep from 400 to 1200 Hz\n        evt.duration = 0.05f;\n        evt.volume = 0.6f;\n        evt.duty = 0.5f;\n\n        audio.playEvent(evt);\n        delay(50);  // Small delay between events\n    }\n}\n
    "},{"location":"api_reference/audio/audio_types/#performance-considerations","title":"Performance Considerations","text":"
    • Event creation: Creating events is fast (just struct initialization)
    • Channel allocation: Events are queued and played when channels are available
    • Frequency range: Keep frequencies in reasonable range (100-5000 Hz) for best results
    • Duration: Shorter durations free channels faster
    "},{"location":"api_reference/audio/audio_types/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Events are small structs, safe to create frequently
    • CPU: Audio generation is efficient but limit simultaneous sounds
    • Quality: Lower sample rates reduce CPU usage
    "},{"location":"api_reference/audio/audio_types/#see-also","title":"See Also","text":"
    • AudioEngine - Audio playback engine
    • AudioConfig - Audio configuration
    • Manual - Audio
    • API Overview
    "},{"location":"api_reference/audio/music_player/","title":"MusicPlayer","text":"

    Lightweight sequencer built on top of AudioEngine to play background melodies as tracks.

    "},{"location":"api_reference/audio/music_player/#description","title":"Description","text":"

    MusicPlayer is a simple sequencer that plays MusicTrack structures. It advances notes based on game time, converts MusicNote entries to AudioEvent calls, and manages playback state (play, stop, pause, resume).

    The player uses one audio channel (typically a Pulse channel) for music, leaving other channels available for sound effects.

    "},{"location":"api_reference/audio/music_player/#namespace","title":"Namespace","text":"
    namespace pixelroot32::audio {\n    class MusicPlayer {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/audio/music_player/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Engine (manages music player instance)
    "},{"location":"api_reference/audio/music_player/#constructors","title":"Constructors","text":""},{"location":"api_reference/audio/music_player/#musicplayeraudioengine-engine","title":"MusicPlayer(AudioEngine& engine)","text":"

    Constructs the MusicPlayer.

    Parameters: - engine (AudioEngine&): Reference to the AudioEngine used to play sounds

    Notes: - Typically created and managed by Engine - Access via engine.getMusicPlayer()

    Example:

    auto& audio = engine.getAudioEngine();\npixelroot32::audio::MusicPlayer musicPlayer(audio);\n

    "},{"location":"api_reference/audio/music_player/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/audio/music_player/#void-playconst-musictrack-track","title":"void play(const MusicTrack& track)","text":"

    Starts playing a track.

    Parameters: - track (const MusicTrack&): The track to play

    Returns: - void

    Notes: - Stops any currently playing track - Starts from the beginning of the track - If track has loop = true, will loop automatically - Uses one audio channel (typically Pulse)

    Example:

    static const MusicNote MELODY[] = {\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::E, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::G, 0.25f),\n    makeRest(0.10f),\n};\n\nstatic const MusicTrack GAME_MUSIC = {\n    MELODY,\n    sizeof(MELODY) / sizeof(MusicNote),\n    true,  // loop\n    WaveType::PULSE,\n    0.5f   // volume\n};\n\nvoid init() override {\n    auto& music = engine.getMusicPlayer();\n    music.play(GAME_MUSIC);\n}\n

    "},{"location":"api_reference/audio/music_player/#void-stop","title":"void stop()","text":"

    Stops playback and silences the channel.

    Returns: - void

    Notes: - Immediately stops the current note - Resets playback to the beginning - Channel is freed for other use

    Example:

    void onGameOver() {\n    auto& music = engine.getMusicPlayer();\n    music.stop();\n}\n

    "},{"location":"api_reference/audio/music_player/#void-pause","title":"void pause()","text":"

    Pauses playback.

    Returns: - void

    Notes: - Current note continues until it ends, then playback pauses - Playback state is preserved (can resume from where it paused) - Use for pause menus

    Example:

    void onPause() {\n    auto& music = engine.getMusicPlayer();\n    music.pause();\n}\n

    "},{"location":"api_reference/audio/music_player/#void-resume","title":"void resume()","text":"

    Resumes playback.

    Returns: - void

    Notes: - Only works if playback was paused - Resumes from where it was paused - Use to unpause after pause menu

    Example:

    void onResume() {\n    auto& music = engine.getMusicPlayer();\n    music.resume();\n}\n

    "},{"location":"api_reference/audio/music_player/#void-updateunsigned-long-deltatime","title":"void update(unsigned long deltaTime)","text":"

    Updates the player state. Should be called every frame.

    Parameters: - deltaTime (unsigned long): Time elapsed since last frame in milliseconds

    Returns: - void

    Notes: - Must be called every frame for proper timing - Advances note playback based on elapsed time - Automatically plays next notes in sequence - Typically called automatically by Engine

    Example:

    // Called automatically by Engine, but can be called manually:\nvoid update(unsigned long deltaTime) override {\n    Scene::update(deltaTime);\n\n    // Music player is updated automatically by Engine\n    // No need to call manually\n}\n

    "},{"location":"api_reference/audio/music_player/#bool-isplaying-const","title":"bool isPlaying() const","text":"

    Checks if a track is currently playing.

    Returns: - bool: true if playing, false otherwise

    Notes: - Returns false if stopped or paused - Use to check playback state before operations

    Example:

    auto& music = engine.getMusicPlayer();\nif (music.isPlaying()) {\n    // Music is active\n} else {\n    // Music is stopped or paused\n}\n

    "},{"location":"api_reference/audio/music_player/#void-settempofactorfloat-factor","title":"void setTempoFactor(float factor)","text":"

    Sets the global tempo scaling factor.

    Parameters: - factor (float): Tempo multiplier - 1.0f: Normal speed - 2.0f: Double speed - 0.5f: Half speed

    Returns: - void

    Notes: - Affects all note durations - Useful for speed-up effects or slow-motion - Applied to all tracks

    Example:

    auto& music = engine.getMusicPlayer();\nmusic.setTempoFactor(1.5f);  // 50% faster\nmusic.setTempoFactor(0.5f);   // 50% slower\nmusic.setTempoFactor(1.0f);   // Normal speed\n

    "},{"location":"api_reference/audio/music_player/#float-gettempofactor-const","title":"float getTempoFactor() const","text":"

    Gets the current tempo scaling factor.

    Returns: - float: Current factor (default 1.0f)

    Example:

    float currentTempo = musicPlayer.getTempoFactor();\n

    "},{"location":"api_reference/audio/music_player/#musictrack-structure","title":"MusicTrack Structure","text":"

    A MusicTrack contains:

    • notes (const MusicNote*): Array of music notes
    • noteCount (size_t): Number of notes in the array
    • loop (bool): Whether to loop the track
    • waveType (WaveType): Wave type to use (typically PULSE)
    • volume (float): Volume level (0.0 to 1.0)
    "},{"location":"api_reference/audio/music_player/#musicnote-structure","title":"MusicNote Structure","text":"

    A MusicNote contains:

    • instrument (InstrumentPreset): Instrument preset to use
    • note (Note): Musical note (C, D, E, etc.)
    • duration (float): Duration in seconds

    Use helper functions: - makeNote(instrument, note, duration): Create a note - makeRest(duration): Create a rest (silence)

    "},{"location":"api_reference/audio/music_player/#usage-example","title":"Usage Example","text":"
    #include \"audio/MusicPlayer.h\"\n#include \"audio/AudioMusicTypes.h\"\n\nusing namespace pixelroot32::audio;\n\n// Define a simple melody\nstatic const MusicNote MAIN_THEME[] = {\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.25f),\n    makeNote(INSTR_PULSE_LEAD, Note::E, 0.25f),\n    makeNote(INSTR_PULSE_LEAD, Note::G, 0.25f),\n    makeRest(0.1f),\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.5f),\n    makeRest(0.2f),\n};\n\nstatic const MusicTrack MAIN_THEME_TRACK = {\n    MAIN_THEME,\n    sizeof(MAIN_THEME) / sizeof(MusicNote),\n    true,           // loop\n    WaveType::PULSE,\n    0.6f            // volume\n};\n\nclass GameScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Start background music\n        auto& music = engine.getMusicPlayer();\n        music.play(MAIN_THEME_TRACK);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Music updates automatically\n    }\n\n    void onPauseMenu() {\n        auto& music = engine.getMusicPlayer();\n        music.pause();\n    }\n\n    void onResumeGame() {\n        auto& music = engine.getMusicPlayer();\n        music.resume();\n    }\n\n    void onGameOver() {\n        auto& music = engine.getMusicPlayer();\n        music.stop();\n    }\n};\n
    "},{"location":"api_reference/audio/music_player/#performance-considerations","title":"Performance Considerations","text":"
    • One channel: Music uses one channel, leaving others for sound effects
    • Update frequency: update() must be called every frame
    • Track size: Larger tracks use more memory (store in flash)
    • Tempo factor: Changing tempo is fast (just a multiplier)
    "},{"location":"api_reference/audio/music_player/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Store tracks in flash (const/constexpr) to save RAM
    • CPU: Music playback is lightweight (simple sequencing)
    • Channel conflict: Music and sound effects share channels; plan accordingly
    "},{"location":"api_reference/audio/music_player/#see-also","title":"See Also","text":"
    • AudioEngine - Audio playback engine
    • AudioTypes - Audio data structures
    • AudioMusicTypes - Music data structures
    • Manual - Audio
    • API Overview
    "},{"location":"api_reference/core/actor/","title":"Actor","text":"

    An Entity capable of physical interaction and collision.

    "},{"location":"api_reference/core/actor/#description","title":"Description","text":"

    Actor extends Entity with collision layers and masks. Actors are used for dynamic game objects like players, enemies, projectiles, and obstacles that need to interact with each other through collision detection.

    Actors participate in the collision system and can detect collisions with other actors based on their collision layers and masks.

    "},{"location":"api_reference/core/actor/#namespace","title":"Namespace","text":"
    namespace pixelroot32::core {\n    class Actor : public Entity {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/actor/#inheritance","title":"Inheritance","text":"
    • Inherits from: Entity
    • Inherited by: PhysicsActor and your custom actor classes
    "},{"location":"api_reference/core/actor/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/actor/#actorfloat-x-float-y-int-w-int-h","title":"Actor(float x, float y, int w, int h)","text":"

    Creates a new actor with specified position and size.

    Parameters: - x (float): Initial X position in world space - y (float): Initial Y position in world space - w (int): Actor width in pixels - h (int): Actor height in pixels

    Notes: - Actor type is automatically set to EntityType::ACTOR - Collision layer and mask default to DefaultLayers::kNone - Must set collision layer and mask for collision detection to work

    Example:

    class PlayerActor : public pixelroot32::core::Actor {\npublic:\n    PlayerActor(float x, float y) \n        : Actor(x, y, 16, 16) {\n        // Set collision layer and mask\n        layer = pixelroot32::physics::DefaultLayers::kPlayer;\n        mask = pixelroot32::physics::DefaultLayers::kEnemy | \n               pixelroot32::physics::DefaultLayers::kObstacle;\n    }\n\n    void update(unsigned long deltaTime) override {\n        Actor::update(deltaTime);\n        // Player logic\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawSprite(playerSprite, \n                           static_cast<int>(x), \n                           static_cast<int>(y), \n                           Color::White);\n    }\n\n    Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(Actor* other) override {\n        // Handle collision\n    }\n};\n

    "},{"location":"api_reference/core/actor/#public-properties","title":"Public Properties","text":""},{"location":"api_reference/core/actor/#collisionlayer-layer","title":"CollisionLayer layer","text":"

    The collision layer this actor belongs to.

    Type: pixelroot32::physics::CollisionLayer (uint16_t)

    Access: Read-write

    Default: DefaultLayers::kNone

    Notes: - Defines which layer this actor is on - Use bit flags to assign multiple layers (e.g., kPlayer | kProjectile) - Only actors with matching layers in their mask will collide

    Example:

    actor->layer = pixelroot32::physics::DefaultLayers::kPlayer;\n

    "},{"location":"api_reference/core/actor/#collisionlayer-mask","title":"CollisionLayer mask","text":"

    The collision layers this actor interacts with.

    Type: pixelroot32::physics::CollisionLayer (uint16_t)

    Access: Read-write

    Default: DefaultLayers::kNone

    Notes: - Defines which layers this actor can collide with - Use bit flags to check multiple layers (e.g., kEnemy | kObstacle) - Collision only occurs if the other actor's layer matches bits in this mask

    Example:

    // Actor collides with enemies and obstacles\nactor->mask = pixelroot32::physics::DefaultLayers::kEnemy | \n              pixelroot32::physics::DefaultLayers::kObstacle;\n

    "},{"location":"api_reference/core/actor/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/actor/#void-setcollisionlayercollisionlayer-l","title":"void setCollisionLayer(CollisionLayer l)","text":"

    Sets the collision layer for this actor.

    Parameters: - l (pixelroot32::physics::CollisionLayer): The layer to set

    Returns: - void

    Notes: - Equivalent to setting layer directly - Use bit flags for multiple layers

    Example:

    actor->setCollisionLayer(pixelroot32::physics::DefaultLayers::kPlayer);\n

    "},{"location":"api_reference/core/actor/#void-setcollisionmaskcollisionlayer-m","title":"void setCollisionMask(CollisionLayer m)","text":"

    Sets the collision mask for this actor.

    Parameters: - m (pixelroot32::physics::CollisionLayer): The mask to set

    Returns: - void

    Notes: - Equivalent to setting mask directly - Use bit flags for multiple layers

    Example:

    actor->setCollisionMask(pixelroot32::physics::DefaultLayers::kEnemy | \n                        pixelroot32::physics::DefaultLayers::kObstacle);\n

    "},{"location":"api_reference/core/actor/#bool-isinlayeruint16_t-targetlayer-const","title":"bool isInLayer(uint16_t targetLayer) const","text":"

    Checks if the Actor belongs to a specific collision layer.

    Parameters: - targetLayer (uint16_t): The bit(s) to check (e.g., DefaultLayers::kPlayer)

    Returns: - bool: true if the bit is set in the actor's layer

    Notes: - Uses bitwise AND operation - Useful for checking if an actor is on a specific layer

    Example:

    if (actor->isInLayer(pixelroot32::physics::DefaultLayers::kPlayer)) {\n    // This is a player actor\n}\n

    "},{"location":"api_reference/core/actor/#virtual-rect-gethitbox-0","title":"virtual Rect getHitBox() = 0","text":"

    Gets the hitbox for collision detection. Must be implemented by derived classes.

    Returns: - Rect: A rectangle representing the collision bounds

    Notes: - Called by the collision system to check collisions - Should return the actual collision bounds (may differ from visual size) - Use AABB (Axis-Aligned Bounding Box) for efficiency

    Example:

    Rect getHitBox() override {\n    // Return collision bounds (may be smaller than visual)\n    return {x + 2, y + 2, width - 4, height - 4};\n}\n

    "},{"location":"api_reference/core/actor/#virtual-void-oncollisionactor-other-0","title":"virtual void onCollision(Actor* other) = 0","text":"

    Callback invoked when a collision occurs. Must be implemented by derived classes.

    Parameters: - other (Actor*): The actor that this actor collided with

    Notes: - Called automatically by the collision system when a collision is detected - Both actors' onCollision() methods are called - Use to handle collision responses (damage, bouncing, etc.)

    Example:

    void onCollision(Actor* other) override {\n    // Check what we collided with\n    if (other->isInLayer(pixelroot32::physics::DefaultLayers::kEnemy)) {\n        // Take damage\n        health--;\n        if (health <= 0) {\n            isEnabled = false;\n        }\n    } else if (other->isInLayer(pixelroot32::physics::DefaultLayers::kCollectible)) {\n        // Collect item\n        score += 10;\n        other->isEnabled = false;  // Remove collectible\n    }\n}\n

    "},{"location":"api_reference/core/actor/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the actor logic. Default implementation does nothing.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    Notes: - Override to implement actor-specific update logic - Called automatically by Scene if isEnabled is true - Use deltaTime for frame-rate independent movement

    Example:

    void update(unsigned long deltaTime) override {\n    Actor::update(deltaTime);  // Call base implementation\n\n    // Move actor\n    float speed = 100.0f;  // pixels per second\n    x += (speed * deltaTime) / 1000.0f;\n}\n

    "},{"location":"api_reference/core/actor/#collision-layers","title":"Collision Layers","text":"

    Collision layers use bit flags to organize actors into groups. Common layers:

    • DefaultLayers::kNone (0): No layer
    • DefaultLayers::kPlayer (1 << 0): Player actors
    • DefaultLayers::kEnemy (1 << 1): Enemy actors
    • DefaultLayers::kObstacle (1 << 2): Obstacles/walls
    • DefaultLayers::kProjectile (1 << 3): Projectiles
    • DefaultLayers::kCollectible (1 << 4): Collectible items

    Example:

    // Player collides with enemies and obstacles\nplayer->layer = DefaultLayers::kPlayer;\nplayer->mask = DefaultLayers::kEnemy | DefaultLayers::kObstacle;\n\n// Enemy collides with player and obstacles\nenemy->layer = DefaultLayers::kEnemy;\nenemy->mask = DefaultLayers::kPlayer | DefaultLayers::kObstacle;\n\n// Projectile collides with enemies\nprojectile->layer = DefaultLayers::kProjectile;\nprojectile->mask = DefaultLayers::kEnemy;\n

    "},{"location":"api_reference/core/actor/#usage-example","title":"Usage Example","text":"
    #include \"core/Actor.h\"\n#include \"physics/CollisionTypes.h\"\n\nclass EnemyActor : public pixelroot32::core::Actor {\nprivate:\n    const pixelroot32::graphics::Sprite* sprite;\n    int health = 3;\n\npublic:\n    EnemyActor(float x, float y) \n        : Actor(x, y, 16, 16),\n          sprite(&enemySprite) {\n        // Set collision layer and mask\n        layer = pixelroot32::physics::DefaultLayers::kEnemy;\n        mask = pixelroot32::physics::DefaultLayers::kPlayer | \n               pixelroot32::physics::DefaultLayers::kProjectile;\n    }\n\n    void update(unsigned long deltaTime) override {\n        Actor::update(deltaTime);\n\n        // Move towards player\n        float speed = 50.0f;\n        // ... movement logic\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawSprite(*sprite, \n                           static_cast<int>(x), \n                           static_cast<int>(y), \n                           Color::Red);\n    }\n\n    Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(Actor* other) override {\n        if (other->isInLayer(pixelroot32::physics::DefaultLayers::kProjectile)) {\n            // Hit by projectile\n            health--;\n            if (health <= 0) {\n                isEnabled = false;  // Remove enemy\n            }\n        }\n    }\n};\n
    "},{"location":"api_reference/core/actor/#performance-considerations","title":"Performance Considerations","text":"
    • Collision layers: Use layers efficiently to reduce collision checks
    • Hitbox size: Keep hitboxes simple (AABB) for best performance
    • Collision callbacks: Keep onCollision() fast; avoid expensive operations
    • Layer organization: Group actors by layer to minimize checks
    "},{"location":"api_reference/core/actor/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Collision checks: Collision system automatically optimizes using layers
    • Memory: Each actor consumes memory; stay within MAX_ENTITIES limit
    • Object pooling: Reuse actors instead of creating/destroying frequently
    "},{"location":"api_reference/core/actor/#see-also","title":"See Also","text":"
    • Entity - Base entity class
    • PhysicsActor - Entity with physics
    • CollisionSystem - Collision detection
    • CollisionTypes - Collision layer definitions
    • Manual - Scenes and Entities
    • Manual - Physics and Collisions
    • API Overview
    "},{"location":"api_reference/core/engine/","title":"Engine","text":"

    The main engine class that manages the game loop and core subsystems.

    "},{"location":"api_reference/core/engine/#description","title":"Description","text":"

    Engine acts as the central hub of the PixelRoot32 game engine. It initializes and manages the Renderer, InputManager, AudioEngine, and SceneManager. It runs the main game loop, handling timing (delta time), updating the current scene, and rendering frames.

    The engine provides a unified interface for both ESP32 and Native (SDL2) platforms, abstracting platform-specific details while maintaining consistent behavior.

    "},{"location":"api_reference/core/engine/#namespace","title":"Namespace","text":"
    namespace pixelroot32::core {\n    class Engine {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/engine/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Application entry point (main.cpp)
    "},{"location":"api_reference/core/engine/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/engine/#engineconst-displayconfig-displayconfig-const-inputconfig-inputconfig-const-audioconfig-audioconfig","title":"Engine(const DisplayConfig& displayConfig, const InputConfig& inputConfig, const AudioConfig& audioConfig)","text":"

    Creates a new engine instance with custom display, input, and audio configurations.

    Parameters: - displayConfig (const pixelroot32::graphics::DisplayConfig&): Configuration settings for the display (width, height, rotation, etc.) - inputConfig (const pixelroot32::input::InputConfig&): Configuration settings for the input system (pins, buttons) - audioConfig (const pixelroot32::audio::AudioConfig&): Configuration settings for the audio system (backend, sample rate, buffer size)

    Example:

    #include \"core/Engine.h\"\n#include \"graphics/DisplayConfig.h\"\n#include \"input/InputConfig.h\"\n#include \"audio/AudioConfig.h\"\n\npixelroot32::graphics::DisplayConfig displayConfig;\ndisplayConfig.width = 128;\ndisplayConfig.height = 128;\n\npixelroot32::input::InputConfig inputConfig;\n// Configure input pins...\n\npixelroot32::audio::AudioConfig audioConfig;\naudioConfig.backend = pixelroot32::audio::AudioConfig::Backend::ESP32_DAC;\naudioConfig.sampleRate = 11025;\n\npixelroot32::core::Engine engine(displayConfig, inputConfig, audioConfig);\nengine.init();\nengine.run();\n

    "},{"location":"api_reference/core/engine/#engineconst-displayconfig-displayconfig-const-inputconfig-inputconfig","title":"Engine(const DisplayConfig& displayConfig, const InputConfig& inputConfig)","text":"

    Creates a new engine instance with custom display and input configurations, using default audio settings.

    Parameters: - displayConfig (const pixelroot32::graphics::DisplayConfig&): Configuration settings for the display - inputConfig (const pixelroot32::input::InputConfig&): Configuration settings for the input system

    Example:

    pixelroot32::core::Engine engine(displayConfig, inputConfig);\nengine.init();\nengine.run();\n

    "},{"location":"api_reference/core/engine/#engineconst-displayconfig-displayconfig","title":"Engine(const DisplayConfig& displayConfig)","text":"

    Creates a new engine instance with custom display configuration and default input/audio settings.

    Parameters: - displayConfig (const pixelroot32::graphics::DisplayConfig&): Configuration settings for the display

    Example:

    pixelroot32::core::Engine engine(displayConfig);\nengine.init();\nengine.run();\n

    "},{"location":"api_reference/core/engine/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/engine/#void-init","title":"void init()","text":"

    Initializes the engine subsystems. This method must be called before run().

    Returns: - void

    Notes: - Initializes the Renderer, InputManager, and sets up the initial state - Must be called after construction and before run() - Safe to call multiple times (idempotent)

    Example:

    Engine engine(displayConfig);\nengine.init();  // Initialize subsystems\nengine.setScene(myScene);\nengine.run();   // Start game loop\n

    "},{"location":"api_reference/core/engine/#void-run","title":"void run()","text":"

    Starts the main game loop. This method contains the infinite loop that calls update() and draw() repeatedly until the application exits.

    Returns: - void

    Notes: - This method blocks until the application exits - Handles frame timing and delta time calculation automatically - Calls update() and draw() once per frame - Do not call this method multiple times

    Example:

    Engine engine(displayConfig);\nengine.init();\nengine.setScene(myScene);\nengine.run();  // Blocks here, runs game loop\n

    "},{"location":"api_reference/core/engine/#unsigned-long-getdeltatime-const","title":"unsigned long getDeltaTime() const","text":"

    Gets the time elapsed since the last frame.

    Returns: - unsigned long: The delta time in milliseconds

    Performance Notes: - Very fast (inline accessor) - Safe to call every frame - Use this value to make movement frame-rate independent

    Example:

    void update(unsigned long deltaTime) override {\n    auto& engine = getEngine();\n    unsigned long dt = engine.getDeltaTime();\n\n    // Move at constant speed regardless of FPS\n    float speed = 100.0f;  // pixels per second\n    x += (speed * dt) / 1000.0f;\n}\n

    "},{"location":"api_reference/core/engine/#void-setscenescene-newscene","title":"void setScene(Scene* newScene)","text":"

    Sets the current active scene to be updated and rendered.

    Parameters: - newScene (Scene*): Pointer to the new Scene to become active. Can be nullptr to clear the current scene.

    Notes: - The previous scene is replaced (not pushed onto a stack) - Use SceneManager for push/pop operations if needed - The scene's init() method will be called automatically - Safe to call during the game loop

    Example:

    class MainMenuScene : public pixelroot32::core::Scene {\n    // ...\n};\n\nclass GameScene : public pixelroot32::core::Scene {\n    // ...\n};\n\nMainMenuScene menuScene;\nGameScene gameScene;\n\nEngine engine(displayConfig);\nengine.init();\nengine.setScene(&menuScene);  // Start with menu\nengine.run();\n

    "},{"location":"api_reference/core/engine/#scene-getcurrentscene-const","title":"Scene* getCurrentScene() const","text":"

    Retrieves the currently active scene.

    Returns: - Scene*: Pointer to the current Scene, or nullptr if none is set

    Example:

    auto* currentScene = engine.getCurrentScene();\nif (currentScene) {\n    // Scene is active\n}\n

    "},{"location":"api_reference/core/engine/#void-setrendererrenderer-newrenderer","title":"void setRenderer(Renderer& newRenderer)","text":"

    Replaces the current renderer instance.

    Parameters: - newRenderer (pixelroot32::graphics::Renderer&): Reference to the new Renderer to use

    Notes: - Advanced usage: typically not needed unless implementing custom renderer - The renderer must be properly initialized before use - Use with caution: may break existing rendering code

    "},{"location":"api_reference/core/engine/#renderer-getrenderer","title":"Renderer& getRenderer()","text":"

    Provides access to the Renderer subsystem.

    Returns: - pixelroot32::graphics::Renderer&: Reference to the current Renderer

    Example:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    auto& engineRenderer = engine.getRenderer();\n    engineRenderer.drawSprite(mySprite, 100, 100, Color::White);\n}\n

    "},{"location":"api_reference/core/engine/#inputmanager-getinputmanager","title":"InputManager& getInputManager()","text":"

    Provides access to the InputManager subsystem.

    Returns: - pixelroot32::input::InputManager&: Reference to the InputManager

    Example:

    void update(unsigned long deltaTime) override {\n    auto& input = engine.getInputManager();\n    if (input.isButtonPressed(Buttons::A)) {\n        // Handle button press\n    }\n}\n

    "},{"location":"api_reference/core/engine/#audioengine-getaudioengine","title":"AudioEngine& getAudioEngine()","text":"

    Provides access to the AudioEngine subsystem.

    Returns: - pixelroot32::audio::AudioEngine&: Reference to the AudioEngine

    Example:

    void playSound() {\n    auto& audio = engine.getAudioEngine();\n    pixelroot32::audio::AudioEvent sound{};\n    sound.type = pixelroot32::audio::WaveType::PULSE;\n    sound.frequency = 800.0f;\n    sound.duration = 0.1f;\n    audio.playEvent(sound);\n}\n

    "},{"location":"api_reference/core/engine/#musicplayer-getmusicplayer","title":"MusicPlayer& getMusicPlayer()","text":"

    Provides access to the MusicPlayer subsystem.

    Returns: - pixelroot32::audio::MusicPlayer&: Reference to the MusicPlayer

    Example:

    void playMusic() {\n    auto& music = engine.getMusicPlayer();\n    music.playTrack(myMusicTrack);\n}\n

    "},{"location":"api_reference/core/engine/#optional-fps-overlay","title":"Optional: FPS overlay","text":"

    When the engine is built with the preprocessor define PIXELROOT32_ENABLE_FPS_DISPLAY, an on-screen FPS counter is drawn each frame.

    Behavior:

    • A green text string \"FPS xxx\" is drawn in the top-right area (position from Renderer::getWidth() and a fixed Y offset).
    • The value is derived from frame delta time (FPS = 1000 / deltaTime ms), clamped to 0\u2013999.

    Performance:

    • The numeric value is recalculated and formatted only every 8 frames; the cached string is drawn every frame to keep the overlay visible without extra per-frame cost (division and snprintf are done at most once every 8 frames).

    How to enable:

    In platformio.ini, add to your environment's build_flags:

    build_flags =\n    -D PIXELROOT32_ENABLE_FPS_DISPLAY\n

    No code changes are required; the overlay is drawn automatically after the scene in Engine::draw(). The implementation uses the private method drawFpsOverlay(Renderer& r), which is only compiled when the define is set.

    See also: Performance Tuning - Profiling and Platforms and Drivers - Build flags.

    "},{"location":"api_reference/core/engine/#usage-example","title":"Usage Example","text":"
    #include \"core/Engine.h\"\n#include \"graphics/DisplayConfig.h\"\n#include \"MyScene.h\"\n\nvoid setup() {\n    // Configure display\n    pixelroot32::graphics::DisplayConfig displayConfig;\n    displayConfig.width = 128;\n    displayConfig.height = 128;\n    displayConfig.rotation = 0;\n\n    // Create engine\n    pixelroot32::core::Engine engine(displayConfig);\n\n    // Initialize\n    engine.init();\n\n    // Create and set scene\n    MyScene myScene;\n    engine.setScene(&myScene);\n\n    // Run game loop\n    engine.run();\n}\n
    "},{"location":"api_reference/core/engine/#performance-considerations","title":"Performance Considerations","text":"
    • Initialization: init() should be called once at startup, not in the game loop
    • Scene switching: Switching scenes is fast but avoid doing it every frame
    • Subsystem access: Getters are inline and very fast; safe to call every frame
    • Delta time: Use getDeltaTime() for frame-rate independent movement
    "},{"location":"api_reference/core/engine/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Ensure init() completes before run() to avoid initialization issues
    • Monitor memory usage when switching scenes frequently
    • Use getDeltaTime() for consistent gameplay across different frame rates
    "},{"location":"api_reference/core/engine/#see-also","title":"See Also","text":"
    • Scene - Scene management
    • Renderer - Rendering system
    • InputManager - Input handling
    • AudioEngine - Audio system
    • Getting Started - Fundamental Concepts
    • Manual - Scenes and Entities
    • API Overview
    "},{"location":"api_reference/core/entity/","title":"Entity","text":"

    Abstract base class for all game objects.

    "},{"location":"api_reference/core/entity/#description","title":"Description","text":"

    Entity is the fundamental building block of the scene. Entities have a position, size, and lifecycle methods (update, draw). All game objects inherit from Entity, including actors, UI elements, and custom game objects.

    Entities are managed by Scene and are automatically updated and drawn each frame when enabled and visible.

    "},{"location":"api_reference/core/entity/#namespace","title":"Namespace","text":"
    namespace pixelroot32::core {\n    class Entity {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/entity/#inheritance","title":"Inheritance","text":"
    • Base class: None (abstract base class)
    • Inherited by: Actor, UI elements, and your custom entity classes
    "},{"location":"api_reference/core/entity/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/entity/#entityfloat-x-float-y-int-w-int-h-entitytype-t","title":"Entity(float x, float y, int w, int h, EntityType t)","text":"

    Creates a new entity with specified position, size, and type.

    Parameters: - x (float): Initial X position in world space - y (float): Initial Y position in world space - w (int): Width in pixels - h (int): Height in pixels - t (EntityType): The type of entity (GENERIC, ACTOR, UI_ELEMENT)

    Example:

    class MyEntity : public pixelroot32::core::Entity {\npublic:\n    MyEntity(float x, float y) \n        : Entity(x, y, 16, 16, EntityType::GENERIC) {}\n\n    void update(unsigned long deltaTime) override {\n        // Update logic\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw logic\n    }\n};\n

    "},{"location":"api_reference/core/entity/#public-properties","title":"Public Properties","text":""},{"location":"api_reference/core/entity/#float-x-y","title":"float x, y","text":"

    Position of the entity in world space.

    Type: float

    Access: Read-write

    Notes: - Position is in world coordinates, not screen coordinates - Can be modified directly or through helper methods - Use for entity positioning and movement

    Example:

    entity->x = 100.0f;\nentity->y = 50.0f;\n

    "},{"location":"api_reference/core/entity/#int-width-height","title":"int width, height","text":"

    Dimensions of the entity in pixels.

    Type: int

    Access: Read-write

    Notes: - Used for collision detection, rendering bounds, and layout - Should match the visual size of the entity - Can be modified at runtime if needed

    Example:

    entity->width = 32;\nentity->height = 32;\n

    "},{"location":"api_reference/core/entity/#entitytype-type","title":"EntityType type","text":"

    The specific type of this entity.

    Type: EntityType enum

    Access: Read-only (set in constructor)

    Values: - EntityType::GENERIC: Generic entity - EntityType::ACTOR: Actor entity (with collision) - EntityType::UI_ELEMENT: UI element

    Notes: - Used for type-safe casting and logic differentiation - Set once in constructor, typically not changed

    "},{"location":"api_reference/core/entity/#bool-isvisible","title":"bool isVisible","text":"

    If false, the entity's draw() method will not be called.

    Type: bool

    Access: Read-write

    Default: true

    Notes: - Use to hide entities without removing them from the scene - More efficient than removing and re-adding entities - Useful for object pooling

    Example:

    entity->isVisible = false;  // Hide entity\nentity->setVisible(true);   // Show entity\n

    "},{"location":"api_reference/core/entity/#bool-isenabled","title":"bool isEnabled","text":"

    If false, the entity's update() method will not be called.

    Type: bool

    Access: Read-write

    Default: true

    Notes: - Use to disable entity logic without removing it - Entity still exists but doesn't update - Useful for paused entities or object pooling

    Example:

    entity->isEnabled = false;  // Disable updates\nentity->setEnabled(true);   // Enable updates\n

    "},{"location":"api_reference/core/entity/#unsigned-char-renderlayer","title":"unsigned char renderLayer","text":"

    The render layer this entity is drawn on.

    Type: unsigned char

    Access: Read-write

    Default: 1

    Notes: - Layers are drawn in ascending order (0 = background, 1 = gameplay, 2 = UI) - Entities on the same layer are drawn in add order - Use to control draw order without changing entity order

    Example:

    entity->renderLayer = 0;  // Background layer\nentity->setRenderLayer(2); // UI layer\n

    "},{"location":"api_reference/core/entity/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/entity/#virtual-void-setvisiblebool-v","title":"virtual void setVisible(bool v)","text":"

    Sets the visibility of the entity.

    Parameters: - v (bool): true to show, false to hide

    Returns: - void

    Notes: - Equivalent to setting isVisible directly - Can be overridden for custom visibility logic

    Example:

    entity->setVisible(false);  // Hide\nentity->setVisible(true);   // Show\n

    "},{"location":"api_reference/core/entity/#virtual-void-setenabledbool-e","title":"virtual void setEnabled(bool e)","text":"

    Sets the enabled state of the entity.

    Parameters: - e (bool): true to enable, false to disable

    Returns: - void

    Notes: - Equivalent to setting isEnabled directly - Can be overridden for custom enable logic

    Example:

    entity->setEnabled(false);  // Disable updates\nentity->setEnabled(true);   // Enable updates\n

    "},{"location":"api_reference/core/entity/#unsigned-char-getrenderlayer-const","title":"unsigned char getRenderLayer() const","text":"

    Gets the current render layer.

    Returns: - unsigned char: The render layer (0-255)

    Example:

    unsigned char layer = entity->getRenderLayer();\n

    "},{"location":"api_reference/core/entity/#virtual-void-setrenderlayerunsigned-char-layer","title":"virtual void setRenderLayer(unsigned char layer)","text":"

    Sets the render layer for this entity.

    Parameters: - layer (unsigned char): The render layer (0 = background, 1 = gameplay, 2 = UI)

    Returns: - void

    Example:

    entity->setRenderLayer(0);  // Background\n

    "},{"location":"api_reference/core/entity/#virtual-void-updateunsigned-long-deltatime-0","title":"virtual void update(unsigned long deltaTime) = 0","text":"

    Updates the entity's logic. Must be implemented by derived classes.

    Parameters: - deltaTime (unsigned long): Time elapsed since the last frame in milliseconds

    Returns: - void

    Notes: - Called automatically by Scene every frame if isEnabled is true - Use deltaTime for frame-rate independent movement - Override to implement entity-specific update logic

    Example:

    void update(unsigned long deltaTime) override {\n    // Move entity\n    float speed = 50.0f;  // pixels per second\n    x += (speed * deltaTime) / 1000.0f;\n\n    // Wrap around screen\n    if (x > 128) {\n        x = 0;\n    }\n}\n

    "},{"location":"api_reference/core/entity/#virtual-void-drawrenderer-renderer-0","title":"virtual void draw(Renderer& renderer) = 0","text":"

    Renders the entity. Must be implemented by derived classes.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer to use for drawing

    Returns: - void

    Notes: - Called automatically by Scene every frame if isVisible is true - Entities are drawn in render layer order, then in add order - Override to implement entity-specific drawing logic

    Example:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Draw sprite at entity position\n    renderer.drawSprite(mySprite, static_cast<int>(x), static_cast<int>(y), Color::White);\n}\n

    "},{"location":"api_reference/core/entity/#entitytype-enum","title":"EntityType Enum","text":"

    Categorizes entities for type-safe casting and logic differentiation.

    Values: - EntityType::GENERIC: Generic entity (default) - EntityType::ACTOR: Actor entity (with collision support) - EntityType::UI_ELEMENT: UI element

    Example:

    if (entity->type == EntityType::ACTOR) {\n    Actor* actor = static_cast<Actor*>(entity);\n    // Use actor-specific methods\n}\n

    "},{"location":"api_reference/core/entity/#rect-structure","title":"Rect Structure","text":"

    Represents a 2D rectangle, typically used for hitboxes or bounds.

    Members: - float x, y: Top-left corner coordinates - int width, height: Dimensions of the rectangle

    Methods: - bool intersects(const Rect& other): Checks if this rectangle intersects with another

    Example:

    pixelroot32::core::Rect rect1{10.0f, 20.0f, 50, 50};\npixelroot32::core::Rect rect2{30.0f, 40.0f, 50, 50};\n\nif (rect1.intersects(rect2)) {\n    // Rectangles overlap\n}\n

    "},{"location":"api_reference/core/entity/#usage-example","title":"Usage Example","text":"
    #include \"core/Entity.h\"\n\nclass Collectible : public pixelroot32::core::Entity {\nprivate:\n    const pixelroot32::graphics::Sprite* sprite;\n\npublic:\n    Collectible(float x, float y) \n        : Entity(x, y, 8, 8, EntityType::GENERIC),\n          sprite(&collectibleSprite) {}\n\n    void update(unsigned long deltaTime) override {\n        // Rotate or animate\n        rotation += deltaTime * 0.001f;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        if (isVisible) {\n            renderer.drawSprite(*sprite, \n                               static_cast<int>(x), \n                               static_cast<int>(y), \n                               Color::Yellow);\n        }\n    }\n\nprivate:\n    float rotation = 0.0f;\n};\n
    "},{"location":"api_reference/core/entity/#performance-considerations","title":"Performance Considerations","text":"
    • Visibility: Use isVisible = false instead of removing entities when hiding
    • Enable state: Use isEnabled = false to pause entity logic
    • Render layers: Organize entities by layer to minimize layer switches
    • Direct access: Direct property access is fast (no function call overhead)
    "},{"location":"api_reference/core/entity/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Each entity consumes memory; stay within MAX_ENTITIES limit
    • Object pooling: Reuse entities instead of creating/destroying frequently
    • Update frequency: Disable entities that don't need to update every frame
    "},{"location":"api_reference/core/entity/#see-also","title":"See Also","text":"
    • Scene - Scene management
    • Actor - Entity with collision support
    • PhysicsActor - Entity with physics
    • Manual - Scenes and Entities
    • API Overview
    "},{"location":"api_reference/core/input_config/","title":"InputConfig","text":"

    Configuration structure for the InputManager.

    "},{"location":"api_reference/core/input_config/#description","title":"Description","text":"

    InputConfig defines the mapping between logical inputs and physical pins (ESP32) or keyboard keys (Native/SDL2). It uses variadic arguments to allow flexible configuration of any number of inputs.

    The configuration is platform-specific: ESP32 uses GPIO pin numbers, while Native uses SDL keyboard scancodes.

    "},{"location":"api_reference/core/input_config/#namespace","title":"Namespace","text":"
    namespace pixelroot32::input {\n    struct InputConfig {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/input_config/#structure","title":"Structure","text":""},{"location":"api_reference/core/input_config/#int-count","title":"int count","text":"

    Total number of configured inputs.

    Type: int

    Access: Read-write

    Default: 0

    Notes: - Must match the number of arguments provided to constructor - Determines the size of the internal button array

    "},{"location":"api_reference/core/input_config/#int-inputpins-esp32-only","title":"int* inputPins (ESP32 only)","text":"

    Array of GPIO pin numbers for ESP32.

    Type: int*

    Access: Read-write

    Default: nullptr

    Notes: - Only available on ESP32 platform - Array size equals count - Pin numbers correspond to ESP32 GPIO pins - Use nullptr if count is 0

    Example:

    // ESP32: 6 buttons on pins 0, 2, 4, 5, 18, 19\npixelroot32::input::InputConfig config(6, 0, 2, 4, 5, 18, 19);\n// config.inputPins[0] = 0  (Up)\n// config.inputPins[1] = 2  (Down)\n// config.inputPins[2] = 4  (Left)\n// config.inputPins[3] = 5  (Right)\n// config.inputPins[4] = 18 (Button A)\n// config.inputPins[5] = 19 (Button B)\n

    "},{"location":"api_reference/core/input_config/#uint8_t-buttonnames-native-only","title":"uint8_t* buttonNames (Native only)","text":"

    Array of button mappings (scancodes) for Native.

    Type: uint8_t*

    Access: Read-write

    Default: nullptr

    Notes: - Only available on Native platform - Array size equals count - Values are SDL keyboard scancodes - Use nullptr if count is 0

    Example:

    // Native: Map to keyboard keys\n#include <SDL2/SDL.h>\n\npixelroot32::input::InputConfig config(6,\n    SDL_SCANCODE_UP,    // Index 0\n    SDL_SCANCODE_DOWN,  // Index 1\n    SDL_SCANCODE_LEFT,  // Index 2\n    SDL_SCANCODE_RIGHT, // Index 3\n    SDL_SCANCODE_X,     // Index 4 (Button A)\n    SDL_SCANCODE_Z      // Index 5 (Button B)\n);\n

    "},{"location":"api_reference/core/input_config/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/input_config/#inputconfigint-count","title":"InputConfig(int count, ...)","text":"

    Constructs a new InputConfig with variadic arguments.

    Parameters: - count (int): Number of inputs to configure - ... (variadic): Variable arguments list of pins (ESP32) or scancodes (Native)

    Notes: - If count <= 0, configuration is empty (nullptr arrays) - Allocates arrays dynamically based on count - Arguments must match count in number - Platform-specific: ESP32 expects int (GPIO pins), Native expects int (SDL scancodes)

    ESP32 Example:

    // Configure 4 directional buttons\npixelroot32::input::InputConfig config(4, 0, 2, 4, 5);\n// Pin 0 = Up, Pin 2 = Down, Pin 4 = Left, Pin 5 = Right\n\n// Configure 6 buttons (4 directions + 2 action buttons)\npixelroot32::input::InputConfig config(6, 0, 2, 4, 5, 18, 19);\n

    Native Example:

    #include <SDL2/SDL.h>\n\n// Configure 4 directional buttons\npixelroot32::input::InputConfig config(4,\n    SDL_SCANCODE_UP,\n    SDL_SCANCODE_DOWN,\n    SDL_SCANCODE_LEFT,\n    SDL_SCANCODE_RIGHT\n);\n\n// Configure 6 buttons (4 directions + 2 action buttons)\npixelroot32::input::InputConfig config(6,\n    SDL_SCANCODE_UP,\n    SDL_SCANCODE_DOWN,\n    SDL_SCANCODE_LEFT,\n    SDL_SCANCODE_RIGHT,\n    SDL_SCANCODE_X,  // Button A\n    SDL_SCANCODE_Z   // Button B\n);\n

    "},{"location":"api_reference/core/input_config/#usage-example","title":"Usage Example","text":""},{"location":"api_reference/core/input_config/#esp32-configuration","title":"ESP32 Configuration","text":"
    #include \"input/InputConfig.h\"\n#include \"input/InputManager.h\"\n#include \"core/Engine.h\"\n\nvoid setup() {\n    // Configure input: 6 buttons\n    // Pins: Up=0, Down=2, Left=4, Right=5, A=18, B=19\n    pixelroot32::input::InputConfig inputConfig(6, 0, 2, 4, 5, 18, 19);\n\n    // Create input manager\n    pixelroot32::input::InputManager inputManager(inputConfig);\n    inputManager.init();\n\n    // Or use with Engine\n    pixelroot32::graphics::DisplayConfig displayConfig;\n    pixelroot32::core::Engine engine(displayConfig, inputConfig);\n    engine.init();\n    engine.run();\n}\n
    "},{"location":"api_reference/core/input_config/#native-configuration","title":"Native Configuration","text":"
    #include \"input/InputConfig.h\"\n#include <SDL2/SDL.h>\n\nvoid setup() {\n    // Configure input: 6 buttons mapped to keyboard\n    pixelroot32::input::InputConfig inputConfig(6,\n        SDL_SCANCODE_UP,    // Index 0: Up\n        SDL_SCANCODE_DOWN,  // Index 1: Down\n        SDL_SCANCODE_LEFT,  // Index 2: Left\n        SDL_SCANCODE_RIGHT, // Index 3: Right\n        SDL_SCANCODE_X,     // Index 4: Button A\n        SDL_SCANCODE_Z      // Index 5: Button B\n    );\n\n    // Use with Engine\n    pixelroot32::graphics::DisplayConfig displayConfig;\n    pixelroot32::core::Engine engine(displayConfig, inputConfig);\n    engine.init();\n    engine.run();\n}\n
    "},{"location":"api_reference/core/input_config/#platform-agnostic-configuration","title":"Platform-Agnostic Configuration","text":"
    #ifdef PLATFORM_ESP32\n    // ESP32: Use GPIO pins\n    pixelroot32::input::InputConfig inputConfig(6, 0, 2, 4, 5, 18, 19);\n#elif PLATFORM_NATIVE\n    // Native: Use SDL scancodes\n    #include <SDL2/SDL.h>\n    pixelroot32::input::InputConfig inputConfig(6,\n        SDL_SCANCODE_UP,\n        SDL_SCANCODE_DOWN,\n        SDL_SCANCODE_LEFT,\n        SDL_SCANCODE_RIGHT,\n        SDL_SCANCODE_X,\n        SDL_SCANCODE_Z\n    );\n#endif\n
    "},{"location":"api_reference/core/input_config/#button-index-mapping","title":"Button Index Mapping","text":"

    Button indices are determined by the order in the constructor:

    Typical Convention: - Index 0: Up / Primary action - Index 1: Down / Secondary action - Index 2: Left - Index 3: Right - Index 4+: Additional buttons

    Example:

    // 4-button D-pad\nInputConfig config(4, UP_PIN, DOWN_PIN, LEFT_PIN, RIGHT_PIN);\n// Index 0 = Up, Index 1 = Down, Index 2 = Left, Index 3 = Right\n\n// 6-button setup (D-pad + 2 action buttons)\nInputConfig config(6, UP_PIN, DOWN_PIN, LEFT_PIN, RIGHT_PIN, A_PIN, B_PIN);\n// Index 0-3 = D-pad, Index 4 = A, Index 5 = B\n

    "},{"location":"api_reference/core/input_config/#esp32-pin-considerations","title":"ESP32 Pin Considerations","text":"
    • GPIO pins: Use any available GPIO pin
    • Pull-up/pull-down: Configure resistors appropriately
    • Input mode: Pins are automatically configured as inputs
    • Restrictions: Some pins have special functions (check ESP32 datasheet)

    Common Pin Choices: - GPIO 0, 2, 4, 5: Safe for buttons (watch for boot mode pins) - GPIO 18, 19: Good for additional buttons - Avoid: GPIO 6-11 (flash), GPIO 34-39 (input only, no pull-up)

    "},{"location":"api_reference/core/input_config/#native-sdl-scancode-reference","title":"Native SDL Scancode Reference","text":"

    Common SDL scancodes:

    • SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT: Arrow keys
    • SDL_SCANCODE_W, SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_D: WASD
    • SDL_SCANCODE_X, SDL_SCANCODE_Z: Common action buttons
    • SDL_SCANCODE_SPACE: Spacebar
    • SDL_SCANCODE_RETURN: Enter key

    Example:

    // WASD + Space + Enter\npixelroot32::input::InputConfig config(6,\n    SDL_SCANCODE_W,        // Up\n    SDL_SCANCODE_S,        // Down\n    SDL_SCANCODE_A,        // Left\n    SDL_SCANCODE_D,         // Right\n    SDL_SCANCODE_SPACE,    // Jump\n    SDL_SCANCODE_RETURN    // Action\n);\n

    "},{"location":"api_reference/core/input_config/#performance-considerations","title":"Performance Considerations","text":"
    • Memory: Arrays are allocated dynamically (small overhead)
    • Configuration: Done once at startup, no runtime cost
    • Access: Button indices are fast (array access)
    "},{"location":"api_reference/core/input_config/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Pin configuration: Ensure pins are not used by other peripherals
    • Debouncing: Hardware debouncing recommended for reliable input
    • Power: Buttons should use pull-up resistors to avoid floating pins
    "},{"location":"api_reference/core/input_config/#see-also","title":"See Also","text":"
    • InputManager - Input handling
    • Engine - Engine that uses InputConfig
    • Manual - Input and Control
    • API Overview
    "},{"location":"api_reference/core/input_manager/","title":"InputManager","text":"

    Handles input from physical buttons or keyboard (on PC).

    "},{"location":"api_reference/core/input_manager/#description","title":"Description","text":"

    The InputManager polls configured pins (ESP32) or keyboard state (Native), handles debouncing, and tracks button states (Pressed, Released, Down, Clicked). It provides a unified input interface for both platforms.

    The manager supports edge detection (just pressed/released) and continuous state (held down), making it suitable for both gameplay and UI navigation.

    "},{"location":"api_reference/core/input_manager/#namespace","title":"Namespace","text":"
    namespace pixelroot32::input {\n    class InputManager {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/input_manager/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Engine (manages input manager instance)
    "},{"location":"api_reference/core/input_manager/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/input_manager/#inputmanagerconst-inputconfig-config","title":"InputManager(const InputConfig& config)","text":"

    Constructs the InputManager with a specific configuration.

    Parameters: - config (const InputConfig&): The input configuration (pins, button count)

    Example:

    #include \"input/InputManager.h\"\n#include \"input/InputConfig.h\"\n\n// ESP32: Configure GPIO pins\npixelroot32::input::InputConfig inputConfig(6, 0, 2, 4, 5, 18, 19);\npixelroot32::input::InputManager inputManager(inputConfig);\ninputManager.init();\n\n// Native: Configure keyboard keys\n// (Configuration handled differently on Native)\n

    "},{"location":"api_reference/core/input_manager/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/input_manager/#void-init","title":"void init()","text":"

    Initializes the input pins.

    Returns: - void

    Notes: - Must be called after construction and before use - Configures GPIO pins (ESP32) or keyboard state (Native) - Safe to call multiple times (idempotent) - Typically called automatically by Engine::init()

    Example:

    InputManager inputManager(inputConfig);\ninputManager.init();  // Initialize before use\n

    "},{"location":"api_reference/core/input_manager/#void-updateunsigned-long-dt","title":"void update(unsigned long dt)","text":"

    Updates input state by polling hardware pins (ESP32) or keyboard state (Native).

    Parameters: - dt (unsigned long): Delta time in milliseconds

    Returns: - void

    Notes: - Must be called every frame for proper input detection - Handles debouncing automatically - Updates button states and edge detection - Typically called automatically by Engine::update()

    ESP32 Example:

    void update(unsigned long deltaTime) override {\n    // Input is updated automatically by Engine\n    // Access input via engine.getInputManager()\n}\n

    Native Example:

    // On Native, update is called with keyboard state:\nvoid update(unsigned long dt, const uint8_t* keyboardState);\n

    "},{"location":"api_reference/core/input_manager/#bool-isbuttonpresseduint8_t-buttonindex-const","title":"bool isButtonPressed(uint8_t buttonIndex) const","text":"

    Checks if a button was just pressed this frame.

    Parameters: - buttonIndex (uint8_t): Index of the button to check (0-based)

    Returns: - bool: true if the button transitioned from UP to DOWN this frame

    Notes: - Returns true only on the frame the button was pressed - Useful for one-time actions (jump, shoot, menu select) - Resets automatically on next frame

    Example:

    auto& input = engine.getInputManager();\n\nif (input.isButtonPressed(0)) {  // Button A (index 0)\n    // Jump (only once per press)\n    player->jump();\n}\n\nif (input.isButtonPressed(1)) {  // Button B (index 1)\n    // Shoot (only once per press)\n    player->shoot();\n}\n

    "},{"location":"api_reference/core/input_manager/#bool-isbuttonreleaseduint8_t-buttonindex-const","title":"bool isButtonReleased(uint8_t buttonIndex) const","text":"

    Checks if a button was just released this frame.

    Parameters: - buttonIndex (uint8_t): Index of the button to check

    Returns: - bool: true if the button transitioned from DOWN to UP this frame

    Notes: - Returns true only on the frame the button was released - Useful for detecting button release events - Less commonly used than isButtonPressed()

    Example:

    auto& input = engine.getInputManager();\n\nif (input.isButtonReleased(0)) {\n    // Button A was just released\n    player->stopCharging();\n}\n

    "},{"location":"api_reference/core/input_manager/#bool-isbuttonclickeduint8_t-buttonindex-const","title":"bool isButtonClicked(uint8_t buttonIndex) const","text":"

    Checks if a button was clicked (pressed and released).

    Parameters: - buttonIndex (uint8_t): Index of the button to check

    Returns: - bool: true if the button was clicked (pressed then released)

    Notes: - Returns true when button is released after being pressed - Useful for UI buttons and menu selection - Detects complete press-release cycle

    Example:

    auto& input = engine.getInputManager();\n\nif (input.isButtonClicked(0)) {  // Button A clicked\n    // Select menu item\n    menu->select();\n}\n

    "},{"location":"api_reference/core/input_manager/#bool-isbuttondownuint8_t-buttonindex-const","title":"bool isButtonDown(uint8_t buttonIndex) const","text":"

    Checks if a button is currently held down.

    Parameters: - buttonIndex (uint8_t): Index of the button to check

    Returns: - bool: true if the button is currently in the DOWN state

    Notes: - Returns true for as long as the button is held - Useful for continuous actions (movement, charging) - Use with deltaTime for frame-rate independent movement

    Example:

    auto& input = engine.getInputManager();\n\nfloat speed = 100.0f;  // pixels per second\nfloat vx = 0.0f, vy = 0.0f;\n\nif (input.isButtonDown(2)) {  // Left button\n    vx = -speed;\n}\nif (input.isButtonDown(3)) {  // Right button\n    vx = speed;\n}\nif (input.isButtonDown(0)) {  // Up button\n    vy = -speed;\n}\nif (input.isButtonDown(1)) {  // Down button\n    vy = speed;\n}\n\n// Apply movement (frame-rate independent)\nx += (vx * deltaTime) / 1000.0f;\ny += (vy * deltaTime) / 1000.0f;\n

    "},{"location":"api_reference/core/input_manager/#button-indices","title":"Button Indices","text":"

    Button indices are defined by the order in InputConfig:

    Typical Mapping: - 0: Up / Button A - 1: Down / Button B - 2: Left - 3: Right - 4: Additional button 1 - 5: Additional button 2

    Example:

    // Configure 6 buttons: Up, Down, Left, Right, A, B\npixelroot32::input::InputConfig inputConfig(6, \n    GPIO_UP,    // Index 0\n    GPIO_DOWN,  // Index 1\n    GPIO_LEFT,  // Index 2\n    GPIO_RIGHT, // Index 3\n    GPIO_A,     // Index 4\n    GPIO_B      // Index 5\n);\n\n// Use indices\nif (input.isButtonDown(2)) {  // Left\n    moveLeft();\n}\nif (input.isButtonPressed(4)) {  // A button\n    jump();\n}\n

    "},{"location":"api_reference/core/input_manager/#usage-example","title":"Usage Example","text":"
    #include \"input/InputManager.h\"\n#include \"core/Engine.h\"\n\nclass PlayerController {\nprivate:\n    pixelroot32::core::Engine& engine;\n\npublic:\n    PlayerController(pixelroot32::core::Engine& eng) : engine(eng) {}\n\n    void update(unsigned long deltaTime) {\n        auto& input = engine.getInputManager();\n\n        // Movement (continuous)\n        float speed = 150.0f;  // pixels per second\n        float vx = 0.0f, vy = 0.0f;\n\n        if (input.isButtonDown(3)) {  // Right\n            vx = speed;\n        }\n        if (input.isButtonDown(2)) {  // Left\n            vx = -speed;\n        }\n        if (input.isButtonDown(0)) {  // Up\n            vy = -speed;\n        }\n        if (input.isButtonDown(1)) {  // Down\n            vy = speed;\n        }\n\n        // Apply movement\n        playerX += (vx * deltaTime) / 1000.0f;\n        playerY += (vy * deltaTime) / 1000.0f;\n\n        // Actions (one-time)\n        if (input.isButtonPressed(4)) {  // A button\n            player->jump();\n        }\n\n        if (input.isButtonPressed(5)) {  // B button\n            player->shoot();\n        }\n    }\n};\n
    "},{"location":"api_reference/core/input_manager/#input-state-comparison","title":"Input State Comparison","text":"Method Returns true when Use Case isButtonPressed() Button just pressed this frame One-time actions (jump, shoot) isButtonReleased() Button just released this frame Release events (stop charging) isButtonClicked() Button pressed then released UI buttons, menu selection isButtonDown() Button currently held Continuous actions (movement)"},{"location":"api_reference/core/input_manager/#performance-considerations","title":"Performance Considerations","text":"
    • Update frequency: update() must be called every frame
    • Debouncing: Handled automatically, no performance impact
    • State queries: All query methods are fast (inline accessors)
    • Memory: Button state arrays are small and efficient
    "},{"location":"api_reference/core/input_manager/#esp32-considerations","title":"ESP32 Considerations","text":"
    • GPIO pins: Configure pins in InputConfig
    • Pull-up/pull-down: Ensure proper resistor configuration
    • Debouncing: Hardware debouncing recommended for noisy buttons
    • Pin limits: Some ESP32 pins have restrictions (check datasheet)
    "},{"location":"api_reference/core/input_manager/#native-considerations","title":"Native Considerations","text":"
    • Keyboard mapping: Uses SDL scancodes
    • Key detection: Automatically handles keyboard state
    • Multiple keys: Can detect multiple keys simultaneously
    "},{"location":"api_reference/core/input_manager/#see-also","title":"See Also","text":"
    • InputConfig - Input configuration
    • Engine - Engine that manages InputManager
    • Manual - Input and Control
    • API Overview
    "},{"location":"api_reference/core/physics_actor/","title":"PhysicsActor","text":"

    An actor with basic 2D physics properties.

    "},{"location":"api_reference/core/physics_actor/#description","title":"Description","text":"

    PhysicsActor extends the base Actor class by adding velocity, acceleration, friction, restitution (bounciness), and world boundary collision resolution. It is designed for objects that need to move and bounce within a defined area, such as balls, projectiles, or platformer characters.

    PhysicsActor automatically handles: - Velocity-based movement - Friction application - World boundary collision and bouncing - Collision callbacks

    "},{"location":"api_reference/core/physics_actor/#namespace","title":"Namespace","text":"
    namespace pixelroot32::core {\n    class PhysicsActor : public Actor {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/physics_actor/#inheritance","title":"Inheritance","text":"
    • Inherits from: Actor
    • Inherited by: Your custom physics-enabled actor classes
    "},{"location":"api_reference/core/physics_actor/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/physics_actor/#physicsactorfloat-x-float-y-float-w-float-h","title":"PhysicsActor(float x, float y, float w, float h)","text":"

    Creates a physics-enabled actor with specified position and size.

    Parameters: - x (float): Initial X position in world space - y (float): Initial Y position in world space - w (float): Actor width in pixels - h (float): Actor height in pixels

    Notes: - Velocity starts at (0, 0) - Restitution defaults to 1.0 (perfect bounce) - Friction defaults to 0.0 (no friction) - No world limits by default

    Example:

    class BallActor : public pixelroot32::core::PhysicsActor {\npublic:\n    BallActor(float x, float y) \n        : PhysicsActor(x, y, 8.0f, 8.0f) {\n        // Set physics properties\n        setRestitution(0.8f);  // 80% bounce\n        setFriction(0.1f);     // Small friction\n        setWorldSize(128, 128); // World bounds\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledCircle(static_cast<int>(x + width/2), \n                                 static_cast<int>(y + height/2), \n                                 width/2, \n                                 Color::White);\n    }\n\n    Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(Actor* other) override {\n        // Bounce off other actors\n    }\n\n    void onWorldCollision() override {\n        // Play bounce sound\n    }\n};\n

    "},{"location":"api_reference/core/physics_actor/#protected-properties","title":"Protected Properties","text":""},{"location":"api_reference/core/physics_actor/#float-vx-vy","title":"float vx, vy","text":"

    Horizontal and vertical velocity components.

    Type: float

    Access: Protected (use setVelocity() to modify)

    Default: 0.0f

    Notes: - Velocity is in pixels per second - Automatically applied during update() - Modified by friction and world collisions

    "},{"location":"api_reference/core/physics_actor/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/physics_actor/#void-setvelocityfloat-x-float-y","title":"void setVelocity(float x, float y)","text":"

    Sets the linear velocity of the actor.

    Parameters: - x (float): Horizontal velocity in pixels per second - y (float): Vertical velocity in pixels per second

    Returns: - void

    Notes: - Velocity is applied every frame during update() - Use for initial velocity or impulse-based movement - Can be called every frame for continuous control

    Example:

    // Set initial velocity\nphysicsActor->setVelocity(100.0f, -200.0f);  // Move right and up\n\n// Continuous control (e.g., player movement)\nvoid update(unsigned long deltaTime) override {\n    PhysicsActor::update(deltaTime);\n\n    float speed = 150.0f;\n    float vx = 0.0f, vy = 0.0f;\n\n    if (input.isButtonDown(Buttons::LEFT)) vx = -speed;\n    if (input.isButtonDown(Buttons::RIGHT)) vx = speed;\n    if (input.isButtonDown(Buttons::UP)) vy = -speed;\n    if (input.isButtonDown(Buttons::DOWN)) vy = speed;\n\n    setVelocity(vx, vy);\n}\n

    "},{"location":"api_reference/core/physics_actor/#void-setrestitutionfloat-r","title":"void setRestitution(float r)","text":"

    Sets the restitution (bounciness) of the actor.

    Parameters: - r (float): Restitution value (0.0 to 1.0+) - 0.0: No bounce (stops on impact) - 1.0: Perfect bounce (no energy loss) - > 1.0: Energy gain (unrealistic but possible)

    Returns: - void

    Notes: - Applied when actor collides with world boundaries - Higher values = more bouncy - Typical values: 0.5-0.9 for realistic bouncing

    Example:

    ball->setRestitution(0.8f);  // 80% bounce\n

    "},{"location":"api_reference/core/physics_actor/#void-setfrictionfloat-f","title":"void setFriction(float f)","text":"

    Sets the friction coefficient.

    Parameters: - f (float): Friction value - 0.0: No friction (object continues moving) - > 0.0: Friction applied to velocity each frame

    Returns: - void

    Notes: - Applied every frame to reduce velocity - Higher values = more friction (slower movement) - Typical values: 0.05-0.2 for smooth deceleration

    Example:

    player->setFriction(0.1f);  // Light friction\n

    "},{"location":"api_reference/core/physics_actor/#void-setlimitslimitrect-limits","title":"void setLimits(LimitRect limits)","text":"

    Sets custom movement limits for the actor.

    Parameters: - limits (LimitRect): A rectangle defining the allowed area

    Returns: - void

    Notes: - Overrides world size limits - Use -1 for any boundary to disable that limit - Actor will bounce off these boundaries

    Example:

    pixelroot32::core::LimitRect limits;\nlimits.left = 0;\nlimits.top = 0;\nlimits.right = 128;\nlimits.bottom = 128;\nphysicsActor->setLimits(limits);\n

    "},{"location":"api_reference/core/physics_actor/#void-setworldsizeint-width-int-height","title":"void setWorldSize(int width, int height)","text":"

    Defines the world size for boundary checking.

    Parameters: - width (int): Width of the world in pixels - height (int): Height of the world in pixels

    Returns: - void

    Notes: - Used as default limits if no custom LimitRect is provided - Actor will bounce off world boundaries - Set to display size for screen boundaries

    Example:

    physicsActor->setWorldSize(128, 128);  // Match display size\n

    "},{"location":"api_reference/core/physics_actor/#worldcollisioninfo-getworldcollisioninfo-const","title":"WorldCollisionInfo getWorldCollisionInfo() const","text":"

    Gets information about collisions with the world boundaries.

    Returns: - WorldCollisionInfo: A struct containing collision flags (left, right, top, bottom)

    Notes: - Updated every frame during update() - Use to detect which boundary was hit - Useful for sound effects or special behaviors

    Example:

    void update(unsigned long deltaTime) override {\n    PhysicsActor::update(deltaTime);\n\n    auto collision = getWorldCollisionInfo();\n    if (collision.left || collision.right) {\n        // Hit side wall\n        playSound(wallHitSound);\n    }\n    if (collision.top || collision.bottom) {\n        // Hit top or bottom\n        playSound(ceilingHitSound);\n    }\n}\n

    "},{"location":"api_reference/core/physics_actor/#virtual-void-oncollisionactor-other-override","title":"virtual void onCollision(Actor* other) override","text":"

    Callback triggered when this actor collides with another actor.

    Parameters: - other (Actor*): Pointer to the actor involved in the collision

    Returns: - void

    Notes: - Called automatically by the collision system - Override to implement custom collision responses - Default implementation does nothing

    Example:

    void onCollision(Actor* other) override {\n    if (other->isInLayer(DefaultLayers::kEnemy)) {\n        // Bounce off enemy\n        vx = -vx * 0.5f;\n        vy = -vy * 0.5f;\n    }\n}\n

    "},{"location":"api_reference/core/physics_actor/#virtual-void-onworldcollision","title":"virtual void onWorldCollision()","text":"

    Callback triggered when this actor collides with world boundaries.

    Returns: - void

    Notes: - Called automatically when a world boundary collision occurs - Override to implement custom behavior (sound effects, particles, etc.) - Default implementation does nothing

    Example:

    void onWorldCollision() override {\n    // Play bounce sound\n    auto& audio = engine.getAudioEngine();\n    pixelroot32::audio::AudioEvent sound{};\n    sound.type = pixelroot32::audio::WaveType::NOISE;\n    sound.frequency = 500.0f;\n    sound.duration = 0.05f;\n    audio.playEvent(sound);\n\n    // Spawn particles\n    spawnBounceParticles();\n}\n

    "},{"location":"api_reference/core/physics_actor/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the actor state. Applies physics integration and checks for world boundary collisions.

    Parameters: - deltaTime (unsigned long): Time elapsed since the last frame in milliseconds

    Returns: - void

    Notes: - Called automatically by Scene if isEnabled is true - Applies velocity to position - Applies friction to velocity - Resolves world boundary collisions - Override to add custom update logic, but call PhysicsActor::update(deltaTime) first

    Example:

    void update(unsigned long deltaTime) override {\n    // Apply physics\n    PhysicsActor::update(deltaTime);\n\n    // Custom logic\n    if (shouldApplyGravity) {\n        vy += gravity * (deltaTime / 1000.0f);\n    }\n}\n

    "},{"location":"api_reference/core/physics_actor/#limitrect-structure","title":"LimitRect Structure","text":"

    Bounding rectangle for world-collision resolution.

    Members: - int left: Left boundary (-1 means no limit) - int top: Top boundary (-1 means no limit) - int right: Right boundary (-1 means no limit) - int bottom: Bottom boundary (-1 means no limit)

    Methods: - int width() const: Calculates width (right - left) - int height() const: Calculates height (bottom - top)

    Example:

    pixelroot32::core::LimitRect limits(10, 10, 118, 118);  // 10px margin\nphysicsActor->setLimits(limits);\n

    "},{"location":"api_reference/core/physics_actor/#worldcollisioninfo-structure","title":"WorldCollisionInfo Structure","text":"

    Information about world collisions in the current frame.

    Members: - bool left: True if collided with the left boundary - bool right: True if collided with the right boundary - bool top: True if collided with the top boundary - bool bottom: True if collided with the bottom boundary

    Example:

    auto collision = physicsActor->getWorldCollisionInfo();\nif (collision.bottom) {\n    // On ground\n    canJump = true;\n}\n

    "},{"location":"api_reference/core/physics_actor/#usage-example","title":"Usage Example","text":"
    #include \"core/PhysicsActor.h\"\n\nclass BouncingBall : public pixelroot32::core::PhysicsActor {\npublic:\n    BouncingBall(float x, float y) \n        : PhysicsActor(x, y, 8.0f, 8.0f) {\n        // Set physics properties\n        setRestitution(0.9f);  // Very bouncy\n        setFriction(0.05f);    // Light friction\n        setWorldSize(128, 128);\n\n        // Set initial velocity\n        setVelocity(100.0f, -150.0f);\n\n        // Set collision layer\n        layer = pixelroot32::physics::DefaultLayers::kProjectile;\n        mask = pixelroot32::physics::DefaultLayers::kObstacle;\n    }\n\n    void update(unsigned long deltaTime) override {\n        PhysicsActor::update(deltaTime);\n\n        // Apply gravity\n        vy += 200.0f * (deltaTime / 1000.0f);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledCircle(static_cast<int>(x + width/2), \n                                 static_cast<int>(y + height/2), \n                                 width/2, \n                                 Color::White);\n    }\n\n    Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(Actor* other) override {\n        // Bounce off obstacles\n        vx = -vx * 0.8f;\n        vy = -vy * 0.8f;\n    }\n\n    void onWorldCollision() override {\n        // Play bounce sound\n        playBounceSound();\n    }\n};\n
    "},{"location":"api_reference/core/physics_actor/#performance-considerations","title":"Performance Considerations","text":"
    • Physics integration: Very efficient (simple velocity integration)
    • World bounds: Boundary checks are fast (AABB)
    • Friction: Applied every frame; keep friction values reasonable
    • Collision callbacks: Keep onCollision() and onWorldCollision() fast
    "},{"location":"api_reference/core/physics_actor/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Floating point: Uses float math; acceptable for ESP32 but integer math would be faster
    • Frame rate: Physics is frame-rate independent (uses deltaTime)
    • Memory: Each PhysicsActor consumes more memory than Actor (velocity, limits, etc.)
    "},{"location":"api_reference/core/physics_actor/#see-also","title":"See Also","text":"
    • Actor - Base actor class
    • CollisionSystem - Collision detection
    • Manual - Physics and Collisions
    • API Overview
    "},{"location":"api_reference/core/scene/","title":"Scene","text":"

    Represents a game level or screen containing entities.

    "},{"location":"api_reference/core/scene/#description","title":"Description","text":"

    A Scene manages a collection of Entities and a CollisionSystem. It is responsible for updating and drawing all entities it contains. Scenes provide lifecycle hooks (init(), update(), draw()) to manage gameplay segments.

    Scenes are the primary organizational unit in PixelRoot32, similar to levels or screens in other game engines. Each scene can contain up to MAX_ENTITIES (default 32; overridable via compiler flags) entities, and drawing uses up to MAX_LAYERS (default 3; overridable) render layers.

    "},{"location":"api_reference/core/scene/#namespace","title":"Namespace","text":"
    namespace pixelroot32::core {\n    class Scene {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/core/scene/#inheritance","title":"Inheritance","text":"
    • Base class: None (abstract base class)
    • Inherited by: Your custom scene classes (e.g., MainMenuScene, GameScene)
    "},{"location":"api_reference/core/scene/#constructors","title":"Constructors","text":""},{"location":"api_reference/core/scene/#scene_1","title":"Scene()","text":"

    Creates an empty scene ready to be populated with entities.

    Notes: - The scene starts with no entities - init() should be called when the scene becomes active - The collision system is automatically initialized

    Example:

    class MyScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Initialize scene resources\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);  // Update entities and collisions\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        Scene::draw(renderer);  // Draw all entities\n    }\n};\n

    "},{"location":"api_reference/core/scene/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/core/scene/#virtual-void-init","title":"virtual void init()","text":"

    Initializes the scene. Called when entering the scene.

    Returns: - void

    Notes: - Called automatically when the scene is set via Engine::setScene() - Override this method to initialize scene-specific resources - Safe to call multiple times (idempotent) - Add entities here or in the constructor

    Example:

    class GameScene : public pixelroot32::core::Scene {\nprivate:\n    PlayerActor* player;\n    std::array<EnemyActor*, 10> enemies;\n\npublic:\n    void init() override {\n        // Create player\n        player = new PlayerActor();\n        addEntity(player);\n\n        // Create enemies\n        for (int i = 0; i < 10; i++) {\n            enemies[i] = new EnemyActor();\n            addEntity(enemies[i]);\n        }\n    }\n};\n

    "},{"location":"api_reference/core/scene/#virtual-void-updateunsigned-long-deltatime","title":"virtual void update(unsigned long deltaTime)","text":"

    Updates all entities in the scene and handles collisions.

    Parameters: - deltaTime (unsigned long): Time elapsed since last frame in milliseconds

    Notes: - Called automatically by the engine every frame - Updates all entities in the scene - Processes collisions between actors - Override to add custom update logic, but call Scene::update(deltaTime) to maintain entity updates

    Example:

    void update(unsigned long deltaTime) override {\n    // Custom update logic\n    gameTimer += deltaTime;\n\n    // Update entities and collisions\n    Scene::update(deltaTime);\n\n    // Additional logic after entity updates\n    if (gameTimer > 60000) {\n        // Game over after 60 seconds\n    }\n}\n

    "},{"location":"api_reference/core/scene/#virtual-void-drawrenderer-renderer","title":"virtual void draw(Renderer& renderer)","text":"

    Draws all visible entities in the scene.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): The renderer to use for drawing

    Notes: - Called automatically by the engine every frame after update() - Draws all visible entities in the scene - Override to add custom drawing logic, but call Scene::draw(renderer) to maintain entity rendering - Entities are drawn in the order they were added

    Example:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Draw background\n    renderer.drawTileMap(backgroundTileMap, 0, 0, Color::White);\n\n    // Draw all entities\n    Scene::draw(renderer);\n\n    // Draw UI overlay\n    renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n}\n

    "},{"location":"api_reference/core/scene/#void-addentityentity-entity","title":"void addEntity(Entity* entity)","text":"

    Adds an entity to the scene.

    Parameters: - entity (Entity*): Pointer to the Entity to add. Must not be nullptr.

    Notes: - Entities are added to an internal queue - Maximum of MAX_ENTITIES (default 32; overridable) entities per scene - If the limit is reached, the entity may not be added (check return value if available) - Entities are updated and drawn in the order they were added - The entity's lifetime is managed by the scene (do not delete manually while in scene)

    Example:

    void init() override {\n    // Create and add player\n    PlayerActor* player = new PlayerActor();\n    player->setPosition(64, 64);\n    addEntity(player);\n\n    // Create and add enemy\n    EnemyActor* enemy = new EnemyActor();\n    enemy->setPosition(100, 100);\n    addEntity(enemy);\n}\n

    "},{"location":"api_reference/core/scene/#void-removeentityentity-entity","title":"void removeEntity(Entity* entity)","text":"

    Removes an entity from the scene.

    Parameters: - entity (Entity*): Pointer to the Entity to remove

    Notes: - The entity is removed from the update and draw queues - The entity is not deleted automatically (you must manage its lifetime) - Safe to call even if the entity is not in the scene - Consider using object pooling instead of frequent add/remove

    Example:

    void onEnemyDestroyed(EnemyActor* enemy) {\n    removeEntity(enemy);\n    // Return to pool or delete\n    enemyPool.returnToPool(enemy);\n}\n

    "},{"location":"api_reference/core/scene/#void-clearentities","title":"void clearEntities()","text":"

    Removes all entities from the scene.

    Notes: - All entities are removed from the update and draw queues - Entities are not deleted automatically (you must manage their lifetimes) - Useful for scene cleanup or reset - Consider using object pooling to reuse entities

    Example:

    void reset() {\n    clearEntities();\n    // Return all entities to pool\n    for (auto* entity : entityPool) {\n        entityPool.returnToPool(entity);\n    }\n}\n

    "},{"location":"api_reference/core/scene/#protected-members","title":"Protected Members","text":""},{"location":"api_reference/core/scene/#arduinoqueue-entities","title":"ArduinoQueue entities

    Queue of entities in the scene. Accessible to derived classes for custom entity management.

    Type: ArduinoQueue<Entity*>

    Notes: - Maximum capacity: MAX_ENTITIES (default 32; overridable) - Direct access allows custom iteration or filtering - Use with caution: modifying while iterating may cause issues

    ","text":""},{"location":"api_reference/core/scene/#collisionsystem-collisionsystem","title":"CollisionSystem collisionSystem

    System to handle collisions between actors. Accessible to derived classes for custom collision handling.

    Type: pixelroot32::physics::CollisionSystem

    Notes: - Automatically processes collisions between actors - Uses collision layers and masks for filtering - Can be accessed for custom collision queries

    ","text":""},{"location":"api_reference/core/scene/#overriding-scene-limits-max_layers-max_entities","title":"Overriding scene limits (MAX_LAYERS / MAX_ENTITIES)","text":"

    The engine defines default limits in core/Scene.h: MAX_LAYERS (default 3) and MAX_ENTITIES (default 32). These are guarded with #ifndef, so you can override them from your project without modifying the engine.

    ESP32 platform limitation

    The default of 3 for MAX_LAYERS is due to ESP32 platform constraints (memory and draw-loop cost). On native/PC you can safely use a higher value; on ESP32, increasing it may affect performance or memory.

    "},{"location":"api_reference/core/scene/#option-a-compiler-flags-recommended","title":"Option A: Compiler flags (recommended)

    In your project (e.g. in platformio.ini), add the defines to build_flags for the environment you use:

    build_flags =\n    -DMAX_LAYERS=5\n    -DMAX_ENTITIES=64\n

    The compiler defines MAX_LAYERS and MAX_ENTITIES before processing any .cpp file. Because Scene.h uses #ifndef MAX_LAYERS / #ifndef MAX_ENTITIES, it will not redefine them and your values will be used.

    Effect: - MAX_LAYERS: Number of render layers drawn in Scene::draw() (layer 0 = background, 1+ = sprite context). Increasing this allows more distinct draw layers (e.g. background, platforms, gameplay, foreground, UI). - MAX_ENTITIES: On Arduino, the capacity of the scene entity queue when constructed with this value. On native (mock queue), the value is ignored (unbounded).

    See also: Platforms and Drivers - Scene limits.

    ","text":""},{"location":"api_reference/core/scene/#usage-example","title":"Usage Example","text":"
    #include \"core/Scene.h\"\n#include \"core/Actor.h\"\n\nclass MyGameScene : public pixelroot32::core::Scene {\nprivate:\n    PlayerActor* player;\n\npublic:\n    void init() override {\n        // Create player\n        player = new PlayerActor();\n        player->setPosition(64, 64);\n        addEntity(player);\n\n        // Create some enemies\n        for (int i = 0; i < 5; i++) {\n            EnemyActor* enemy = new EnemyActor();\n            enemy->setPosition(10 + i * 20, 10);\n            addEntity(enemy);\n        }\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Custom game logic\n        if (player->isDead()) {\n            // Handle game over\n        }\n\n        // Update entities and collisions\n        Scene::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw background\n        renderer.drawFilledRectangle(0, 0, 128, 128, Color::Black);\n\n        // Draw all entities\n        Scene::draw(renderer);\n\n        // Draw HUD\n        char scoreText[32];\n        snprintf(scoreText, sizeof(scoreText), \"Score: %d\", score);\n        renderer.drawText(scoreText, 10, 10, Color::White, 1);\n    }\n};\n
    "},{"location":"api_reference/core/scene/#performance-considerations","title":"Performance Considerations","text":"
    • Entity limit: MAX_ENTITIES (default 32) can be overridden via compiler flags; plan accordingly
    • Add/Remove: Frequent add/remove operations can be expensive; use object pooling
    • Update order: Entities are updated in add order; consider order for dependencies
    • Collision checks: CollisionSystem automatically handles actor collisions efficiently
    "},{"location":"api_reference/core/scene/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Each entity consumes memory; stay well below the limit
    • Object pooling: Essential for ESP32 to avoid memory fragmentation
    • Scene switching: Clearing and recreating scenes can fragment memory; reuse scenes when possible
    "},{"location":"api_reference/core/scene/#see-also","title":"See Also","text":"
    • Entity - Base entity class
    • Actor - Entity with collision support
    • PhysicsActor - Entity with physics
    • CollisionSystem - Collision detection
    • Manual - Scenes and Entities
    • API Overview
    "},{"location":"api_reference/graphics/camera2d/","title":"Camera2D","text":"

    2D camera for scrolling and viewport control.

    "},{"location":"api_reference/graphics/camera2d/#description","title":"Description","text":"

    Camera2D controls viewport position and enables scrolling by shifting the renderer's display offset. It supports following targets, boundary constraints, and can be used for parallax effects.

    The camera uses a dead-zone system: it only moves when the target is outside a central zone, creating smooth following behavior.

    "},{"location":"api_reference/graphics/camera2d/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    class Camera2D {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/camera2d/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Scenes (for scrolling and camera control)
    "},{"location":"api_reference/graphics/camera2d/#constructors","title":"Constructors","text":""},{"location":"api_reference/graphics/camera2d/#camera2dint-viewportwidth-int-viewportheight","title":"Camera2D(int viewportWidth, int viewportHeight)","text":"

    Creates a new camera with specified viewport dimensions.

    Parameters: - viewportWidth (int): Width of the viewport in pixels - viewportHeight (int): Height of the viewport in pixels

    Notes: - Viewport size should match display size - Camera position starts at (0, 0) - No boundaries set by default (camera can move anywhere)

    Example:

    #include \"graphics/Camera2D.h\"\n\n// Create camera matching display size\npixelroot32::graphics::Camera2D camera(128, 128);\n\n// Or get from renderer\nint width = renderer.getWidth();\nint height = renderer.getHeight();\npixelroot32::graphics::Camera2D camera(width, height);\n

    "},{"location":"api_reference/graphics/camera2d/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/graphics/camera2d/#void-setpositionfloat-x-float-y","title":"void setPosition(float x, float y)","text":"

    Sets the camera position directly.

    Parameters: - x (float): X position in world space - y (float): Y position in world space

    Returns: - void

    Notes: - Position is clamped to boundaries if set - Use for direct camera control or cutscenes - Overrides any following behavior

    Example:

    camera.setPosition(100.0f, 200.0f);\n

    "},{"location":"api_reference/graphics/camera2d/#void-setboundsfloat-minx-float-maxx","title":"void setBounds(float minX, float maxX)","text":"

    Sets horizontal boundaries for the camera.

    Parameters: - minX (float): Minimum X position - maxX (float): Maximum X position

    Returns: - void

    Notes: - Camera position is clamped to these bounds - Use to prevent camera from going outside level bounds - Set both horizontal and vertical bounds for full constraint

    Example:

    // Level is 512 pixels wide, camera viewport is 128\n// Prevent camera from showing outside level\ncamera.setBounds(0.0f, 512.0f - 128.0f);\n

    "},{"location":"api_reference/graphics/camera2d/#void-setverticalboundsfloat-miny-float-maxy","title":"void setVerticalBounds(float minY, float maxY)","text":"

    Sets vertical boundaries for the camera.

    Parameters: - minY (float): Minimum Y position - maxY (float): Maximum Y position

    Returns: - void

    Notes: - Camera position is clamped to these bounds - Use to prevent camera from going outside level bounds vertically

    Example:

    // Level is 512 pixels tall, camera viewport is 128\ncamera.setVerticalBounds(0.0f, 512.0f - 128.0f);\n

    "},{"location":"api_reference/graphics/camera2d/#void-followtargetfloat-targetx","title":"void followTarget(float targetX)","text":"

    Makes the camera follow a target horizontally only.

    Parameters: - targetX (float): X position of the target to follow

    Returns: - void

    Notes: - Camera follows target with dead-zone behavior - Only horizontal movement; vertical position unchanged - Useful for side-scrolling games

    Example:

    void update(unsigned long deltaTime) override {\n    // Update player position\n    player->update(deltaTime);\n\n    // Camera follows player horizontally\n    camera.followTarget(player->x);\n    camera.apply(renderer);\n}\n

    "},{"location":"api_reference/graphics/camera2d/#void-followtargetfloat-targetx-float-targety","title":"void followTarget(float targetX, float targetY)","text":"

    Makes the camera follow a target in both axes.

    Parameters: - targetX (float): X position of the target to follow - targetY (float): Y position of the target to follow

    Returns: - void

    Notes: - Camera follows target with dead-zone behavior - Both horizontal and vertical following - Useful for top-down or platformer games

    Example:

    void update(unsigned long deltaTime) override {\n    player->update(deltaTime);\n\n    // Camera follows player in both axes\n    camera.followTarget(player->x, player->y);\n    camera.apply(renderer);\n}\n

    "},{"location":"api_reference/graphics/camera2d/#float-getx-const","title":"float getX() const","text":"

    Gets the current X position of the camera.

    Returns: - float: Current X position in world space

    Example:

    float cameraX = camera.getX();\n

    "},{"location":"api_reference/graphics/camera2d/#float-gety-const","title":"float getY() const","text":"

    Gets the current Y position of the camera.

    Returns: - float: Current Y position in world space

    Example:

    float cameraY = camera.getY();\n

    "},{"location":"api_reference/graphics/camera2d/#void-applyrenderer-renderer-const","title":"void apply(Renderer& renderer) const","text":"

    Applies the camera's offset to the renderer.

    Parameters: - renderer (Renderer&): The renderer to apply the camera offset to

    Returns: - void

    Notes: - Sets the renderer's display offset based on camera position - Should be called before drawing world elements - Negative offset is applied (camera moves right = world moves left)

    Example:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Apply camera offset\n    camera.apply(renderer);\n\n    // Draw world (offset applied automatically)\n    renderer.drawTileMap(levelMap, 0, 0, Color::White);\n    renderer.drawSprite(playerSprite, playerX, playerY, Color::White);\n\n    // UI elements (not affected by camera)\n    renderer.setDisplayOffset(0, 0);  // Reset for UI\n    renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n}\n

    "},{"location":"api_reference/graphics/camera2d/#dead-zone-following","title":"Dead-Zone Following","text":"

    The camera uses a dead-zone system for smooth following:

    • Dead zone: Central area where camera doesn't move
    • Following: Camera moves only when target leaves dead zone
    • Smooth: Creates natural, non-jarring camera movement

    Example:

    // Camera follows player with dead zone\nvoid update(unsigned long deltaTime) override {\n    player->update(deltaTime);\n\n    // Camera follows (dead zone handled internally)\n    camera.followTarget(player->x, player->y);\n}\n

    "},{"location":"api_reference/graphics/camera2d/#usage-example","title":"Usage Example","text":"
    #include \"graphics/Camera2D.h\"\n\nclass GameScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    PlayerActor* player;\n    TileMap levelMap;\n\npublic:\n    void init() override {\n        // Create camera matching display size\n        auto& renderer = engine.getRenderer();\n        camera = pixelroot32::graphics::Camera2D(\n            renderer.getWidth(), \n            renderer.getHeight()\n        );\n\n        // Set level boundaries\n        // Level is 512x512, viewport is 128x128\n        camera.setBounds(0.0f, 512.0f - 128.0f);\n        camera.setVerticalBounds(0.0f, 512.0f - 128.0f);\n\n        // Create player\n        player = new PlayerActor(64, 64);\n        addEntity(player);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Camera follows player\n        camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Apply camera\n        camera.apply(renderer);\n\n        // Draw world (camera offset applied)\n        renderer.drawTileMap(levelMap, 0, 0, Color::White);\n\n        // Draw entities (Scene::draw handles this)\n        Scene::draw(renderer);\n\n        // Reset offset for UI\n        renderer.setDisplayOffset(0, 0);\n        renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n    }\n};\n
    "},{"location":"api_reference/graphics/camera2d/#parallax-scrolling","title":"Parallax Scrolling","text":"

    Use multiple cameras or manual offset for parallax:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Background layer (slow parallax)\n    float bgOffsetX = camera.getX() * 0.5f;  // 50% speed\n    renderer.setDisplayOffset(-bgOffsetX, 0);\n    renderer.drawTileMap(backgroundMap, 0, 0, Color::White);\n\n    // Midground layer (normal speed)\n    camera.apply(renderer);\n    renderer.drawTileMap(midgroundMap, 0, 0, Color::White);\n\n    // Foreground (entities, normal speed)\n    Scene::draw(renderer);\n\n    // UI (no offset)\n    renderer.setDisplayOffset(0, 0);\n    renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n}\n
    "},{"location":"api_reference/graphics/camera2d/#performance-considerations","title":"Performance Considerations","text":"
    • Apply frequency: apply() is fast; safe to call every frame
    • Boundary checks: Boundary clamping is efficient
    • Following: Dead-zone calculations are lightweight
    "},{"location":"api_reference/graphics/camera2d/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Float math: Uses floating point; acceptable but integer math would be faster
    • Memory: Camera is small (few floats); minimal memory usage
    "},{"location":"api_reference/graphics/camera2d/#see-also","title":"See Also","text":"
    • Renderer - Rendering system
    • Manual - Cameras and Scrolling
    • API Overview
    "},{"location":"api_reference/graphics/color/","title":"Color","text":"

    Color constants and palette management system.

    "},{"location":"api_reference/graphics/color/#description","title":"Description","text":"

    The Color enum provides color constants that map to palette indices. The engine supports both legacy mode (single global palette) and dual palette mode (separate palettes for backgrounds and sprites).

    Colors are resolved to 16-bit RGB565 values based on the active palette(s).

    "},{"location":"api_reference/graphics/color/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    enum class Color : uint8_t {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/color/#color-enum-values","title":"Color Enum Values","text":""},{"location":"api_reference/graphics/color/#standard-colors-pr32-palette-indices","title":"Standard Colors (PR32 Palette Indices)","text":"
    • Color::Black (0)
    • Color::White (1)
    • Color::Navy (2)
    • Color::Blue (3)
    • Color::Cyan (4)
    • Color::DarkGreen (5)
    • Color::Green (6)
    • Color::LightGreen (7)
    • Color::Yellow (8)
    • Color::Orange (9)
    • Color::LightRed (10)
    • Color::Red (11)
    • Color::DarkRed (12)
    • Color::Purple (13)
    • Color::Magenta (14)
    • Color::Gray (15)
    "},{"location":"api_reference/graphics/color/#color-aliases","title":"Color Aliases","text":"

    For compatibility, several aliases map to the closest available color:

    • Color::DarkBlue \u2192 Navy
    • Color::LightBlue \u2192 Blue
    • Color::Teal \u2192 Cyan
    • Color::Olive \u2192 DarkGreen
    • Color::Gold \u2192 Yellow
    • Color::Brown \u2192 DarkRed
    • Color::Pink \u2192 Magenta
    • Color::LightPurple \u2192 Magenta
    • Color::Maroon \u2192 DarkRed
    • Color::MidGray \u2192 Gray
    • Color::LightGray \u2192 Gray
    • Color::DarkGray \u2192 Gray
    • Color::Silver \u2192 Gray
    "},{"location":"api_reference/graphics/color/#special-colors","title":"Special Colors","text":"
    • Color::Transparent (255): Not a real color; must be handled by renderer (results in no-op)
    • Color::DebugRed \u2192 Red
    • Color::DebugGreen \u2192 Green
    • Color::DebugBlue \u2192 Blue
    "},{"location":"api_reference/graphics/color/#palettetype-enum","title":"PaletteType Enum","text":"

    Built-in palette types:

    • PaletteType::NES: NES color palette
    • PaletteType::GB: Game Boy (4 shades of green)
    • PaletteType::GBC: Game Boy Color palette
    • PaletteType::PICO8: PICO-8 palette
    • PaletteType::PR32: PixelRoot32 default palette
    "},{"location":"api_reference/graphics/color/#palettecontext-enum","title":"PaletteContext Enum","text":"

    Context for palette selection in dual palette mode:

    • PaletteContext::Background: For backgrounds, tilemaps, and background primitives
    • PaletteContext::Sprite: For sprites, characters, and gameplay elements
    "},{"location":"api_reference/graphics/color/#palette-functions","title":"Palette Functions","text":""},{"location":"api_reference/graphics/color/#void-setpalettepalettetype-palette","title":"void setPalette(PaletteType palette)","text":"

    Selects the active color palette (legacy mode). Sets both background and sprite palettes to the same value.

    Parameters: - palette (PaletteType): The palette to use

    Notes: - Does not enable dual palette mode - All rendering uses the same palette - Use for simple games with single palette

    Example:

    pixelroot32::graphics::setPalette(pixelroot32::graphics::PaletteType::NES);\n

    "},{"location":"api_reference/graphics/color/#void-setcustompaletteconst-uint16_t-palette","title":"void setCustomPalette(const uint16_t* palette)","text":"

    Sets a custom color palette (legacy mode). Sets both background and sprite palettes to the same value.

    Parameters: - palette (const uint16_t*): Pointer to an array of 16 uint16_t RGB565 color values

    Notes: - Array must remain valid (use static/global storage) - Engine does not copy the palette - Does not enable dual palette mode

    Example:

    static const uint16_t MY_PALETTE[16] = {\n    0x0000,  // Black\n    0xFFFF,  // White\n    0x001F,  // Blue\n    // ... 13 more colors\n};\n\npixelroot32::graphics::setCustomPalette(MY_PALETTE);\n

    "},{"location":"api_reference/graphics/color/#void-enabledualpalettemodebool-enable","title":"void enableDualPaletteMode(bool enable)","text":"

    Enables or disables dual palette mode.

    Parameters: - enable (bool): true to enable dual palette mode, false for legacy mode

    Notes: - When enabled: backgrounds and sprites use separate palettes - When disabled: single palette for all rendering (legacy mode)

    Example:

    pixelroot32::graphics::enableDualPaletteMode(true);\n

    "},{"location":"api_reference/graphics/color/#void-setbackgroundpalettepalettetype-palette","title":"void setBackgroundPalette(PaletteType palette)","text":"

    Sets the background palette (for backgrounds, tilemaps, etc.).

    Parameters: - palette (PaletteType): The palette type to use for backgrounds

    Notes: - Only used in dual palette mode - Affects tilemaps, background primitives, etc.

    Example:

    pixelroot32::graphics::enableDualPaletteMode(true);\npixelroot32::graphics::setBackgroundPalette(pixelroot32::graphics::PaletteType::NES);\n

    "},{"location":"api_reference/graphics/color/#void-setspritepalettepalettetype-palette","title":"void setSpritePalette(PaletteType palette)","text":"

    Sets the sprite palette (for sprites, characters, etc.).

    Parameters: - palette (PaletteType): The palette type to use for sprites

    Notes: - Only used in dual palette mode - Affects sprites, characters, gameplay elements

    Example:

    pixelroot32::graphics::setSpritePalette(pixelroot32::graphics::PaletteType::GB);\n

    "},{"location":"api_reference/graphics/color/#void-setdualpalettepalettetype-bgpalette-palettetype-spritepalette","title":"void setDualPalette(PaletteType bgPalette, PaletteType spritePalette)","text":"

    Sets both background and sprite palettes at once. Automatically enables dual palette mode.

    Parameters: - bgPalette (PaletteType): The palette type to use for backgrounds - spritePalette (PaletteType): The palette type to use for sprites

    Example:

    pixelroot32::graphics::setDualPalette(\n    pixelroot32::graphics::PaletteType::NES,  // Background\n    pixelroot32::graphics::PaletteType::GB     // Sprites\n);\n

    "},{"location":"api_reference/graphics/color/#void-setbackgroundcustompaletteconst-uint16_t-palette","title":"void setBackgroundCustomPalette(const uint16_t* palette)","text":"

    Sets a custom background palette.

    Parameters: - palette (const uint16_t*): Pointer to an array of 16 uint16_t RGB565 color values

    Notes: - Array must remain valid (use static/global storage) - Only used in dual palette mode

    Example:

    static const uint16_t BG_PALETTE[16] = { /* ... */ };\npixelroot32::graphics::enableDualPaletteMode(true);\npixelroot32::graphics::setBackgroundCustomPalette(BG_PALETTE);\n

    "},{"location":"api_reference/graphics/color/#void-setspritecustompaletteconst-uint16_t-palette","title":"void setSpriteCustomPalette(const uint16_t* palette)","text":"

    Sets a custom sprite palette.

    Parameters: - palette (const uint16_t*): Pointer to an array of 16 uint16_t RGB565 color values

    Notes: - Array must remain valid (use static/global storage) - Only used in dual palette mode

    Example:

    static const uint16_t SPRITE_PALETTE[16] = { /* ... */ };\npixelroot32::graphics::setSpriteCustomPalette(SPRITE_PALETTE);\n

    "},{"location":"api_reference/graphics/color/#void-setdualcustompaletteconst-uint16_t-bgpalette-const-uint16_t-spritepal","title":"void setDualCustomPalette(const uint16_t bgPalette, const uint16_t spritePal)","text":"

    Sets both background and sprite custom palettes at once. Automatically enables dual palette mode.

    Parameters: - bgPalette (const uint16_t): Pointer to background palette array (16 RGB565 values) - spritePal (const uint16_t): Pointer to sprite palette array (16 RGB565 values)

    Example:

    static const uint16_t BG_PAL[16] = { /* ... */ };\nstatic const uint16_t SPRITE_PAL[16] = { /* ... */ };\npixelroot32::graphics::setDualCustomPalette(BG_PAL, SPRITE_PAL);\n

    "},{"location":"api_reference/graphics/color/#uint16_t-resolvecolorcolor-color","title":"uint16_t resolveColor(Color color)","text":"

    Resolves a Color enum to its corresponding 16-bit color value (legacy mode).

    Parameters: - color (Color): The Color enum value

    Returns: - uint16_t: The 16-bit RGB565 color value

    Notes: - Uses the current active palette (single palette mode) - Color::Transparent must not be resolved (handled by renderer) - Typically called internally by renderer

    Example:

    uint16_t rgb565 = pixelroot32::graphics::resolveColor(pixelroot32::graphics::Color::Red);\n

    "},{"location":"api_reference/graphics/color/#uint16_t-resolvecolorcolor-color-palettecontext-context","title":"uint16_t resolveColor(Color color, PaletteContext context)","text":"

    Resolves a Color enum with context (dual palette mode).

    Parameters: - color (Color): The Color enum value - context (PaletteContext): The palette context (Background or Sprite)

    Returns: - uint16_t: The 16-bit RGB565 color value

    Notes: - Uses the appropriate palette based on context - In legacy mode, context is ignored - Color::Transparent must not be resolved

    Example:

    uint16_t bgColor = pixelroot32::graphics::resolveColor(\n    pixelroot32::graphics::Color::Blue,\n    pixelroot32::graphics::PaletteContext::Background\n);\n

    "},{"location":"api_reference/graphics/color/#rgb565-format","title":"RGB565 Format","text":"

    Colors are stored as 16-bit RGB565 values:

    • Bits 15-11: Red (5 bits, 0-31)
    • Bits 10-5: Green (6 bits, 0-63)
    • Bits 4-0: Blue (5 bits, 0-31)

    Conversion Example:

    // Convert RGB to RGB565\nuint16_t rgb565 = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);\n

    "},{"location":"api_reference/graphics/color/#usage-examples","title":"Usage Examples","text":""},{"location":"api_reference/graphics/color/#legacy-mode-single-palette","title":"Legacy Mode (Single Palette)","text":"
    // Set single palette for all rendering\npixelroot32::graphics::setPalette(pixelroot32::graphics::PaletteType::NES);\n\n// Use colors\nrenderer.drawSprite(sprite, 100, 100, pixelroot32::graphics::Color::Red);\nrenderer.drawFilledRectangle(10, 10, 50, 50, pixelroot32::graphics::Color::Blue);\n
    "},{"location":"api_reference/graphics/color/#dual-palette-mode","title":"Dual Palette Mode","text":"
    // Enable dual palette mode\npixelroot32::graphics::enableDualPaletteMode(true);\n\n// Set different palettes for backgrounds and sprites\npixelroot32::graphics::setBackgroundPalette(pixelroot32::graphics::PaletteType::NES);\npixelroot32::graphics::setSpritePalette(pixelroot32::graphics::PaletteType::GB);\n\n// Or use convenience function\npixelroot32::graphics::setDualPalette(\n    pixelroot32::graphics::PaletteType::NES,\n    pixelroot32::graphics::PaletteType::GB\n);\n
    "},{"location":"api_reference/graphics/color/#custom-palettes","title":"Custom Palettes","text":"
    // Define custom palette (RGB565 values)\nstatic const uint16_t CUSTOM_PALETTE[16] = {\n    0x0000,  // 0: Black\n    0xFFFF,  // 1: White\n    0x001F,  // 2: Blue\n    0x07E0,  // 3: Green\n    0xF800,  // 4: Red\n    // ... 11 more colors\n};\n\n// Use in legacy mode\npixelroot32::graphics::setCustomPalette(CUSTOM_PALETTE);\n\n// Or use in dual palette mode\npixelroot32::graphics::enableDualPaletteMode(true);\npixelroot32::graphics::setBackgroundCustomPalette(CUSTOM_PALETTE);\npixelroot32::graphics::setSpriteCustomPalette(CUSTOM_PALETTE);\n
    "},{"location":"api_reference/graphics/color/#performance-considerations","title":"Performance Considerations","text":"
    • Color resolution: Fast lookup operation
    • Palette switching: Changing palettes is fast (just pointer assignment)
    • Memory: Palettes are stored in flash (const arrays) for best performance
    • Dual mode: Slightly more overhead than legacy mode, but minimal
    "},{"location":"api_reference/graphics/color/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Flash storage: Store custom palettes in flash (const/constexpr)
    • Memory: Palettes are small (16 uint16_t = 32 bytes)
    • Palette switching: Avoid switching palettes every frame
    "},{"location":"api_reference/graphics/color/#see-also","title":"See Also","text":"
    • Renderer - Rendering system
    • Manual - Color Palettes
    • API Overview
    "},{"location":"api_reference/graphics/display_config/","title":"DisplayConfig","text":"

    Configuration settings for initializing the display.

    "},{"location":"api_reference/graphics/display_config/#description","title":"Description","text":"

    DisplayConfig holds display parameters used by the renderer and camera to draw correctly on the target device. It defines the display type, dimensions, rotation, and creates the appropriate DrawSurface implementation for the platform.

    "},{"location":"api_reference/graphics/display_config/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    struct DisplayConfig {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/display_config/#displaytype-enum","title":"DisplayType Enum","text":"

    Supported display types:

    • DisplayType::ST7789: 240x240 TFT display
    • DisplayType::ST7735: 128x128 TFT display
    • DisplayType::NONE: For SDL2 native (no driver needed)
    "},{"location":"api_reference/graphics/display_config/#structure","title":"Structure","text":""},{"location":"api_reference/graphics/display_config/#displaytype-type","title":"DisplayType type","text":"

    The type of display.

    Type: DisplayType enum

    Access: Read-write

    Notes: - Determines which driver to use (ESP32) - NONE for Native/SDL2 platform

    "},{"location":"api_reference/graphics/display_config/#int-rotation","title":"int rotation","text":"

    Display rotation in degrees.

    Type: int

    Access: Read-write

    Default: 0

    Notes: - Common values: 0, 90, 180, 270 - Rotation is applied during initialization - Some displays may not support all rotations

    "},{"location":"api_reference/graphics/display_config/#uint16_t-width","title":"uint16_t width","text":"

    Display width in pixels.

    Type: uint16_t

    Access: Read-write

    Default: 240

    Notes: - Should match actual display width - Used for viewport and camera calculations

    "},{"location":"api_reference/graphics/display_config/#uint16_t-height","title":"uint16_t height","text":"

    Display height in pixels.

    Type: uint16_t

    Access: Read-write

    Default: 240

    Notes: - Should match actual display height - Used for viewport and camera calculations

    "},{"location":"api_reference/graphics/display_config/#int-xoffset","title":"int xOffset","text":"

    X offset for display alignment.

    Type: int

    Access: Read-write

    Default: 0

    Notes: - Used to adjust display position - Some displays need offset for proper alignment

    "},{"location":"api_reference/graphics/display_config/#int-yoffset","title":"int yOffset","text":"

    Y offset for display alignment.

    Type: int

    Access: Read-write

    Default: 0

    Notes: - Used to adjust display position - Some displays need offset for proper alignment

    "},{"location":"api_reference/graphics/display_config/#drawsurface-getdrawsurface-const","title":"DrawSurface& getDrawSurface() const","text":"

    Gets the underlying DrawSurface implementation.

    Returns: - DrawSurface&: Reference to the DrawSurface

    Notes: - Advanced usage: typically not needed - Provides access to low-level display driver - Platform-specific implementation

    "},{"location":"api_reference/graphics/display_config/#constructors","title":"Constructors","text":""},{"location":"api_reference/graphics/display_config/#displayconfigdisplaytype-type-const-int-rot-0-uint16_t-w-240-uint16_t-h-240-const-int-xoffset-0-const-int-yoffset-0","title":"DisplayConfig(DisplayType type, const int rot = 0, uint16_t w = 240, uint16_t h = 240, const int xOffset = 0, const int yOffset = 0)","text":"

    Constructs a DisplayConfig with specified parameters.

    Parameters: - type (DisplayType): The display type - rot (int, optional): Rotation in degrees. Default: 0 - w (uint16_t, optional): Width in pixels. Default: 240 - h (uint16_t, optional): Height in pixels. Default: 240 - xOffset (int, optional): X offset. Default: 0 - yOffset (int, optional): Y offset. Default: 0

    Notes: - Automatically creates the appropriate DrawSurface for the platform - ESP32: Creates TFT_eSPI_Drawer based on display type - Native: Creates SDL2_Drawer

    Example:

    // ST7789 display, 240x240, no rotation\npixelroot32::graphics::DisplayConfig config(\n    pixelroot32::graphics::DisplayType::ST7789\n);\n\n// ST7735 display, 128x128, rotated 90 degrees\npixelroot32::graphics::DisplayConfig config(\n    pixelroot32::graphics::DisplayType::ST7735,\n    90,   // rotation\n    128,  // width\n    128   // height\n);\n\n// Native/SDL2 (no specific display type)\npixelroot32::graphics::DisplayConfig config(\n    pixelroot32::graphics::DisplayType::NONE,\n    0,    // rotation\n    128,  // width\n    128   // height\n);\n

    "},{"location":"api_reference/graphics/display_config/#usage-examples","title":"Usage Examples","text":""},{"location":"api_reference/graphics/display_config/#esp32-with-st7789","title":"ESP32 with ST7789","text":"
    #ifdef PLATFORM_ESP32\n#include \"graphics/DisplayConfig.h\"\n\nvoid setup() {\n    // Configure ST7789 display (240x240)\n    pixelroot32::graphics::DisplayConfig displayConfig(\n        pixelroot32::graphics::DisplayType::ST7789,\n        0,    // rotation\n        240,  // width\n        240   // height\n    );\n\n    // Use with Engine\n    pixelroot32::core::Engine engine(displayConfig);\n    engine.init();\n    engine.run();\n}\n#endif\n
    "},{"location":"api_reference/graphics/display_config/#esp32-with-st7735","title":"ESP32 with ST7735","text":"
    #ifdef PLATFORM_ESP32\n    // Configure ST7735 display (128x128)\n    pixelroot32::graphics::DisplayConfig displayConfig(\n        pixelroot32::graphics::DisplayType::ST7735,\n        0,    // rotation\n        128,  // width\n        128   // height\n    );\n#endif\n
    "},{"location":"api_reference/graphics/display_config/#nativesdl2","title":"Native/SDL2","text":"
    #ifdef PLATFORM_NATIVE\n    // Native display (SDL2 window)\n    pixelroot32::graphics::DisplayConfig displayConfig(\n        pixelroot32::graphics::DisplayType::NONE,\n        0,    // rotation\n        128,  // width\n        128   // height\n    );\n#endif\n
    "},{"location":"api_reference/graphics/display_config/#platform-agnostic-setup","title":"Platform-Agnostic Setup","text":"
    #include \"graphics/DisplayConfig.h\"\n#include \"core/Engine.h\"\n\nvoid setup() {\n    pixelroot32::graphics::DisplayConfig displayConfig;\n\n    #ifdef PLATFORM_ESP32\n        displayConfig.type = pixelroot32::graphics::DisplayType::ST7789;\n        displayConfig.width = 240;\n        displayConfig.height = 240;\n    #elif PLATFORM_NATIVE\n        displayConfig.type = pixelroot32::graphics::DisplayType::NONE;\n        displayConfig.width = 128;\n        displayConfig.height = 128;\n    #endif\n\n    displayConfig.rotation = 0;\n\n    pixelroot32::core::Engine engine(displayConfig);\n    engine.init();\n    engine.run();\n}\n
    "},{"location":"api_reference/graphics/display_config/#display-type-details","title":"Display Type Details","text":""},{"location":"api_reference/graphics/display_config/#st7789","title":"ST7789","text":"
    • Resolution: Typically 240x240 or 240x320
    • Interface: SPI
    • Driver: TFT_eSPI
    • Common sizes: 240x240, 240x320
    "},{"location":"api_reference/graphics/display_config/#st7735","title":"ST7735","text":"
    • Resolution: Typically 128x128 or 128x160
    • Interface: SPI
    • Driver: TFT_eSPI
    • Common sizes: 128x128, 128x160
    "},{"location":"api_reference/graphics/display_config/#none-native","title":"NONE (Native)","text":"
    • Platform: Native/SDL2
    • Driver: SDL2_Drawer
    • Resolution: Configurable (any size)
    • Window: Creates SDL2 window
    "},{"location":"api_reference/graphics/display_config/#rotation","title":"Rotation","text":"

    Display rotation values:

    • 0: Normal orientation
    • 90: Rotated 90 degrees clockwise
    • 180: Rotated 180 degrees
    • 270: Rotated 90 degrees counter-clockwise

    Notes: - Rotation affects coordinate system - Some displays may not support all rotations - Test rotation on your specific hardware

    "},{"location":"api_reference/graphics/display_config/#performance-considerations","title":"Performance Considerations","text":"
    • Initialization: Display initialization happens once at startup
    • Driver selection: Automatic based on platform and type
    • Memory: DisplayConfig is small (few fields)
    "},{"location":"api_reference/graphics/display_config/#esp32-considerations","title":"ESP32 Considerations","text":""},{"location":"api_reference/graphics/display_config/#tft_espi-configuration","title":"TFT_eSPI Configuration","text":"

    DisplayConfig uses TFT_eSPI driver. Additional configuration may be needed in platformio.ini:

    build_flags =\n    -DUSER_SETUP_LOADED=1\n    -DST7789_DRIVER=1\n    -DTFT_WIDTH=240\n    -DTFT_HEIGHT=240\n    # ... pin configuration\n
    "},{"location":"api_reference/graphics/display_config/#pin-configuration","title":"Pin Configuration","text":"

    GPIO pins must be configured separately (not in DisplayConfig):

    • MOSI: Data pin
    • SCLK: Clock pin
    • DC: Data/Command pin
    • RST: Reset pin
    • CS: Chip select pin (optional)
    "},{"location":"api_reference/graphics/display_config/#see-also","title":"See Also","text":"
    • Renderer - Rendering system
    • Camera2D - Camera that uses display dimensions
    • Manual - Platforms and Drivers
    • API Overview
    "},{"location":"api_reference/graphics/font/","title":"Font","text":"

    Descriptor for a bitmap font using 1bpp sprites.

    "},{"location":"api_reference/graphics/font/#description","title":"Description","text":"

    A Font contains an array of Sprite structures, one for each character in the font's character set. Each glyph is rendered as a 1bpp sprite, allowing consistent rendering across platforms.

    The font uses fixed-width glyphs for simplicity and performance. All glyphs share the same width and height, with spacing between characters controlled by the spacing field.

    "},{"location":"api_reference/graphics/font/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    struct Font {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/font/#structure","title":"Structure","text":""},{"location":"api_reference/graphics/font/#const-sprite-glyphs","title":"const Sprite* glyphs","text":"

    Array of sprites, one per character (indexed by character code - firstChar).

    Type: const Sprite*

    Access: Read-only

    Notes: - Array must contain sprites for all characters from firstChar to lastChar - Each sprite represents one character glyph - Should be stored in flash (const/constexpr) for best performance

    Example:

    static const Sprite FONT_GLYPHS[] = {\n    spaceGlyph,    // Index 0 = ' ' (firstChar = 32)\n    exclamationGlyph, // Index 1 = '!'\n    // ... more glyphs\n};\n\nstatic const Font myFont = {\n    FONT_GLYPHS,\n    32,  // firstChar\n    126, // lastChar\n    5,   // glyphWidth\n    7,   // glyphHeight\n    1,   // spacing\n    8    // lineHeight\n};\n

    "},{"location":"api_reference/graphics/font/#uint8_t-firstchar","title":"uint8_t firstChar","text":"

    First character code in the font.

    Type: uint8_t

    Access: Read-only

    Default: Typically 32 (space character)

    Notes: - ASCII code of the first character - Common: 32 (space ' ') for ASCII fonts - Glyphs array starts at index 0 for this character

    Example:

    firstChar = 32;  // Starts at space character\n

    "},{"location":"api_reference/graphics/font/#uint8_t-lastchar","title":"uint8_t lastChar","text":"

    Last character code in the font.

    Type: uint8_t

    Access: Read-only

    Default: Typically 126 (tilde '~')

    Notes: - ASCII code of the last character - Common: 126 (tilde '~') for full ASCII fonts - Glyphs array must contain (lastChar - firstChar + 1) sprites

    Example:

    lastChar = 126;  // Ends at tilde character\n// Font contains characters 32-126 (95 characters)\n

    "},{"location":"api_reference/graphics/font/#uint8_t-glyphwidth","title":"uint8_t glyphWidth","text":"

    Fixed width of each glyph in pixels.

    Type: uint8_t

    Access: Read-only

    Notes: - All glyphs must have the same width - Typical values: 5, 6, 8 pixels - Smaller = more characters per line, less readable

    Example:

    glyphWidth = 5;  // 5 pixels wide (like FONT_5X7)\n

    "},{"location":"api_reference/graphics/font/#uint8_t-glyphheight","title":"uint8_t glyphHeight","text":"

    Fixed height of each glyph in pixels.

    Type: uint8_t

    Access: Read-only

    Notes: - All glyphs must have the same height - Typical values: 7, 8, 10 pixels - Smaller = more lines, less readable

    Example:

    glyphHeight = 7;  // 7 pixels tall (like FONT_5X7)\n

    "},{"location":"api_reference/graphics/font/#uint8_t-spacing","title":"uint8_t spacing","text":"

    Horizontal spacing between characters in pixels.

    Type: uint8_t

    Access: Read-only

    Default: Typically 1

    Notes: - Space added between characters - 0 = no spacing (characters touch) - 1 = 1 pixel gap (common) - Higher values = more readable but wider text

    Example:

    spacing = 1;  // 1 pixel between characters\n

    "},{"location":"api_reference/graphics/font/#uint8_t-lineheight","title":"uint8_t lineHeight","text":"

    Total line height including vertical spacing.

    Type: uint8_t

    Access: Read-only

    Notes: - Should be glyphHeight + verticalSpacing - Used for line breaks and multi-line text - Typical: glyphHeight + 1 or glyphHeight + 2

    Example:

    lineHeight = 8;  // 7 pixel glyph + 1 pixel spacing\n

    "},{"location":"api_reference/graphics/font/#built-in-fonts","title":"Built-in Fonts","text":""},{"location":"api_reference/graphics/font/#font_5x7","title":"FONT_5X7","text":"

    Standard 5x7 pixel font (built-in).

    Properties: - Width: 5 pixels - Height: 7 pixels - Characters: Typically ASCII 32-126 - Spacing: 1 pixel - Line height: 8 pixels

    Usage:

    #include \"graphics/Font.h\"\n\nrenderer.drawText(\"Hello\", 10, 10, Color::White, 1, &FONT_5X7);\n

    "},{"location":"api_reference/graphics/font/#creating-custom-fonts","title":"Creating Custom Fonts","text":""},{"location":"api_reference/graphics/font/#step-1-create-glyph-sprites","title":"Step 1: Create Glyph Sprites","text":"
    // Space character (ASCII 32)\nstatic const uint16_t SPACE_GLYPH[] = {\n    0b00000,\n    0b00000,\n    0b00000,\n    0b00000,\n    0b00000,\n    0b00000,\n    0b00000\n};\n\n// 'A' character (ASCII 65)\nstatic const uint16_t A_GLYPH[] = {\n    0b00100,\n    0b01010,\n    0b10001,\n    0b11111,\n    0b10001,\n    0b10001,\n    0b00000\n};\n\n// ... more glyphs\n
    "},{"location":"api_reference/graphics/font/#step-2-create-glyph-array","title":"Step 2: Create Glyph Array","text":"
    static const Sprite FONT_GLYPHS[] = {\n    {SPACE_GLYPH, 5, 7},  // Index 0 = ' ' (32)\n    // ... more glyphs\n    {A_GLYPH, 5, 7},      // Index 33 = 'A' (65)\n    // ... more glyphs\n};\n
    "},{"location":"api_reference/graphics/font/#step-3-create-font-structure","title":"Step 3: Create Font Structure","text":"
    static const Font MY_FONT = {\n    FONT_GLYPHS,  // glyphs array\n    32,           // firstChar (space)\n    126,          // lastChar (tilde)\n    5,            // glyphWidth\n    7,            // glyphHeight\n    1,            // spacing\n    8             // lineHeight\n};\n
    "},{"location":"api_reference/graphics/font/#step-4-use-font","title":"Step 4: Use Font","text":"
    renderer.drawText(\"Hello\", 10, 10, Color::White, 1, &MY_FONT);\n
    "},{"location":"api_reference/graphics/font/#usage-example","title":"Usage Example","text":"
    #include \"graphics/Font.h\"\n#include \"graphics/Renderer.h\"\n\n// Using built-in font\nvoid draw(Renderer& renderer) override {\n    // Default font\n    renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n\n    // Explicit font\n    renderer.drawText(\"Score: 100\", 10, 30, Color::White, 1, &FONT_5X7);\n\n    // Centered text with font\n    renderer.drawTextCentered(\"Game Over\", 64, Color::Yellow, 2, &FONT_5X7);\n}\n\n// Custom font example\nstatic const uint16_t CUSTOM_GLYPHS[][7] = {\n    // Space, A, B, C, etc.\n};\n\nstatic const Sprite CUSTOM_SPRITES[] = {\n    {CUSTOM_GLYPHS[0], 6, 8},  // Space\n    {CUSTOM_GLYPHS[1], 6, 8},  // A\n    // ... more\n};\n\nstatic const Font CUSTOM_FONT = {\n    CUSTOM_SPRITES,\n    32,   // firstChar\n    90,   // lastChar (A-Z only)\n    6,    // width\n    8,    // height\n    1,    // spacing\n    9     // lineHeight\n};\n\nvoid draw(Renderer& renderer) override {\n    renderer.drawText(\"CUSTOM\", 10, 10, Color::White, 1, &CUSTOM_FONT);\n}\n
    "},{"location":"api_reference/graphics/font/#text-sizing","title":"Text Sizing","text":"

    Calculate text dimensions:

    int getTextWidth(const Font* font, const char* text) {\n    if (!font || !text) return 0;\n\n    int width = 0;\n    for (int i = 0; text[i] != '\\0'; i++) {\n        if (text[i] >= font->firstChar && text[i] <= font->lastChar) {\n            width += font->glyphWidth + font->spacing;\n        }\n    }\n    return width - font->spacing;  // Remove last spacing\n}\n\nint getTextHeight(const Font* font) {\n    return font ? font->lineHeight : 8;\n}\n
    "},{"location":"api_reference/graphics/font/#performance-considerations","title":"Performance Considerations","text":"
    • Font storage: Store fonts in flash (const/constexpr) for best performance
    • Glyph lookup: Fast array access (character code - firstChar)
    • Fixed width: Fixed-width fonts are faster than variable-width
    • Font switching: Changing fonts is fast (just pointer assignment)
    "},{"location":"api_reference/graphics/font/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Store font data in flash, not RAM
    • Font size: Larger fonts use more flash memory
    • Character range: Limit character range to save memory if not needed
    "},{"location":"api_reference/graphics/font/#see-also","title":"See Also","text":"
    • Renderer - Rendering system that uses fonts
    • Sprite - Sprite structure used for glyphs
    • Manual - Basic Rendering
    • API Overview
    "},{"location":"api_reference/graphics/renderer/","title":"Renderer","text":"

    High-level graphics rendering system for drawing shapes, text, sprites, and tilemaps.

    "},{"location":"api_reference/graphics/renderer/#description","title":"Description","text":"

    The Renderer class provides a unified API for drawing shapes, text, and images. It abstracts the underlying hardware implementation (DrawSurface) and manages display configuration, including rotation and offsets.

    The renderer uses integer-only math for optimal performance on ESP32 and supports multiple sprite formats (1bpp, 2bpp, 4bpp) and multi-layer sprites.

    "},{"location":"api_reference/graphics/renderer/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    class Renderer {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/renderer/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Engine (manages renderer instance)
    "},{"location":"api_reference/graphics/renderer/#constructors","title":"Constructors","text":""},{"location":"api_reference/graphics/renderer/#rendererconst-displayconfig-config","title":"Renderer(const DisplayConfig& config)","text":"

    Constructs the Renderer with a specific display configuration.

    Parameters: - config (const DisplayConfig&): The display configuration settings (width, height, rotation, etc.)

    Example:

    #include \"graphics/Renderer.h\"\n#include \"graphics/DisplayConfig.h\"\n\npixelroot32::graphics::DisplayConfig config;\nconfig.width = 128;\nconfig.height = 128;\nconfig.rotation = 0;\n\npixelroot32::graphics::Renderer renderer(config);\nrenderer.init();\n

    "},{"location":"api_reference/graphics/renderer/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/graphics/renderer/#void-init","title":"void init()","text":"

    Initializes the renderer and the underlying draw surface.

    Returns: - void

    Notes: - Must be called after construction and before any drawing operations - Initializes the platform-specific DrawSurface implementation - Safe to call multiple times (idempotent)

    Example:

    Renderer renderer(displayConfig);\nrenderer.init();  // Initialize before use\n

    "},{"location":"api_reference/graphics/renderer/#void-beginframe","title":"void beginFrame()","text":"

    Prepares the buffer for a new frame (clears screen).

    Returns: - void

    Notes: - Should be called once at the start of each frame - Clears the display buffer - Typically called automatically by Engine, but can be called manually

    Example:

    void draw(Renderer& renderer) override {\n    renderer.beginFrame();\n    // Draw everything...\n    renderer.endFrame();\n}\n

    "},{"location":"api_reference/graphics/renderer/#void-endframe","title":"void endFrame()","text":"

    Finalizes the frame and sends the buffer to the display.

    Returns: - void

    Notes: - Should be called once at the end of each frame - Sends the completed frame buffer to the display - Typically called automatically by Engine, but can be called manually

    "},{"location":"api_reference/graphics/renderer/#drawsurface-getdrawsurface","title":"DrawSurface& getDrawSurface()","text":"

    Gets the underlying DrawSurface implementation.

    Returns: - DrawSurface&: Reference to the DrawSurface

    Notes: - Advanced usage: typically not needed unless implementing custom drawing - Provides low-level access to the display driver

    "},{"location":"api_reference/graphics/renderer/#void-drawtextconst-char-text-int16_t-x-int16_t-y-color-color-uint8_t-size","title":"void drawText(const char* text, int16_t x, int16_t y, Color color, uint8_t size)","text":"

    Draws a string of text using the default font.

    Parameters: - text (const char*): The text to draw (null-terminated string) - x (int16_t): X coordinate (top-left corner of text) - y (int16_t): Y coordinate (top-left corner of text) - color (Color): Text color - size (uint8_t): Text size multiplier (1 = normal, 2 = double, etc.)

    Performance Notes: - Efficient for small amounts of text - Avoid calling in tight loops with long strings - Use static buffers for text formatting

    Example:

    renderer.drawText(\"Hello World\", 10, 10, Color::White, 1);\nrenderer.drawText(\"Score: 100\", 10, 30, Color::Yellow, 2);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawtextconst-char-text-int16_t-x-int16_t-y-color-color-uint8_t-size-const-font-font","title":"void drawText(const char text, int16_t x, int16_t y, Color color, uint8_t size, const Font font)","text":"

    Draws a string of text using a specific font.

    Parameters: - text (const char): The text to draw - x (int16_t): X coordinate - y (int16_t): Y coordinate - color (Color): Text color - size (uint8_t): Text size multiplier - font (const Font): Pointer to the font to use. If nullptr, uses the default font

    Example:

    const Font* customFont = &FONT_5X7;\nrenderer.drawText(\"Custom Font\", 10, 10, Color::White, 1, customFont);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawtextcenteredconst-char-text-int16_t-y-color-color-uint8_t-size","title":"void drawTextCentered(const char* text, int16_t y, Color color, uint8_t size)","text":"

    Draws text centered horizontally at a given Y coordinate using the default font.

    Parameters: - text (const char*): The text to draw - y (int16_t): Y coordinate - color (Color): Text color - size (uint8_t): Text size

    Example:

    renderer.drawTextCentered(\"Game Over\", 64, Color::White, 2);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawtextcenteredconst-char-text-int16_t-y-color-color-uint8_t-size-const-font-font","title":"void drawTextCentered(const char text, int16_t y, Color color, uint8_t size, const Font font)","text":"

    Draws text centered horizontally at a given Y coordinate using a specific font.

    Parameters: - text (const char): The text to draw - y (int16_t): Y coordinate - color (Color): Text color - size (uint8_t): Text size - font (const Font): Pointer to the font to use. If nullptr, uses the default font

    "},{"location":"api_reference/graphics/renderer/#void-drawfilledcircleint-x-int-y-int-radius-color-color","title":"void drawFilledCircle(int x, int y, int radius, Color color)","text":"

    Draws a filled circle.

    Parameters: - x (int): Center X coordinate - y (int): Center Y coordinate - radius (int): Radius of the circle in pixels - color (Color): Fill color

    Example:

    renderer.drawFilledCircle(64, 64, 20, Color::Red);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawcircleint-x-int-y-int-radius-color-color","title":"void drawCircle(int x, int y, int radius, Color color)","text":"

    Draws a circle outline.

    Parameters: - x (int): Center X coordinate - y (int): Center Y coordinate - radius (int): Radius of the circle in pixels - color (Color): Outline color

    Example:

    renderer.drawCircle(64, 64, 20, Color::White);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawrectangleint-x-int-y-int-width-int-height-color-color","title":"void drawRectangle(int x, int y, int width, int height, Color color)","text":"

    Draws a rectangle outline.

    Parameters: - x (int): Top-left X coordinate - y (int): Top-left Y coordinate - width (int): Width of the rectangle in pixels - height (int): Height of the rectangle in pixels - color (Color): Outline color

    Example:

    renderer.drawRectangle(10, 10, 100, 50, Color::Blue);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawfilledrectangleint-x-int-y-int-width-int-height-color-color","title":"void drawFilledRectangle(int x, int y, int width, int height, Color color)","text":"

    Draws a filled rectangle.

    Parameters: - x (int): Top-left X coordinate - y (int): Top-left Y coordinate - width (int): Width of the rectangle in pixels - height (int): Height of the rectangle in pixels - color (Color): Fill color

    Example:

    renderer.drawFilledRectangle(10, 10, 100, 50, Color::Green);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawlineint-x1-int-y1-int-x2-int-y2-color-color","title":"void drawLine(int x1, int y1, int x2, int y2, Color color)","text":"

    Draws a line between two points.

    Parameters: - x1 (int): Start X coordinate - y1 (int): Start Y coordinate - x2 (int): End X coordinate - y2 (int): End Y coordinate - color (Color): Line color

    Example:

    renderer.drawLine(0, 0, 128, 128, Color::White);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawpixelint-x-int-y-color-color","title":"void drawPixel(int x, int y, Color color)","text":"

    Draws a single pixel.

    Parameters: - x (int): X coordinate - y (int): Y coordinate - color (Color): Pixel color

    Performance Notes: - Very fast, but avoid calling thousands of times per frame - Use for special effects or debugging

    Example:

    renderer.drawPixel(64, 64, Color::Red);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawspriteconst-sprite-sprite-int-x-int-y-color-color-bool-flipx-false","title":"void drawSprite(const Sprite& sprite, int x, int y, Color color, bool flipX = false)","text":"

    Draws a 1bpp monochrome sprite using the Sprite descriptor.

    Parameters: - sprite (const Sprite&): Sprite descriptor (data, width, height) - x (int): Top-left X coordinate in logical screen space - y (int): Top-left Y coordinate in logical screen space - color (Color): Color used for \"on\" pixels. Default: uses sprite palette context - flipX (bool, optional): If true, sprite is mirrored horizontally. Default: false

    Performance Notes: - Very efficient for 1bpp sprites (integer-only operations) - Sprite data should be stored in flash (const/constexpr) for best performance - Avoid calling in tight loops; batch similar operations when possible

    Example:

    static const uint16_t playerData[] = {\n    0b00111100,\n    0b01111110,\n    // ... more rows\n};\n\nstatic const Sprite playerSprite = {\n    playerData,\n    8,  // width\n    8   // height\n};\n\nrenderer.drawSprite(playerSprite, 100, 100, Color::White);\nrenderer.drawSprite(playerSprite, 120, 100, Color::White, true);  // Flipped\n

    "},{"location":"api_reference/graphics/renderer/#void-drawspriteconst-sprite-sprite-int-x-int-y-float-scalex-float-scaley-color-color-bool-flipx-false","title":"void drawSprite(const Sprite& sprite, int x, int y, float scaleX, float scaleY, Color color, bool flipX = false)","text":"

    Draws a scaled 1bpp monochrome sprite.

    Parameters: - sprite (const Sprite&): Sprite descriptor - x (int): Top-left X coordinate - y (int): Top-left Y coordinate - scaleX (float): Horizontal scaling factor (e.g., 1.25 for 25% larger) - scaleY (float): Vertical scaling factor - color (Color): Color used for \"on\" pixels - flipX (bool, optional): If true, sprite is mirrored horizontally before scaling. Default: false

    Performance Notes: - Slower than non-scaled version due to scaling calculations - Use integer scaling factors when possible (1.0, 2.0, etc.) for better performance

    Example:

    renderer.drawSprite(playerSprite, 100, 100, 2.0f, 2.0f, Color::White);  // 2x size\n

    "},{"location":"api_reference/graphics/renderer/#void-drawmultispriteconst-multisprite-sprite-int-x-int-y","title":"void drawMultiSprite(const MultiSprite& sprite, int x, int y)","text":"

    Draws a multi-layer sprite composed of several 1bpp layers.

    Parameters: - sprite (const MultiSprite&): Multi-layer sprite descriptor - x (int): Top-left X coordinate in logical screen space - y (int): Top-left Y coordinate in logical screen space

    Performance Notes: - Each layer is rendered separately, so more layers = more draw calls - Still efficient as each layer uses 1bpp format - Use for multi-color sprites without higher bit-depths

    Example:

    static const SpriteLayer layers[] = {\n    { outlineData, Color::Black },\n    { fillData, Color::Red },\n    { highlightData, Color::Yellow }\n};\n\nstatic const MultiSprite playerMultiSprite = {\n    8,      // width\n    8,      // height\n    layers, // layers array\n    3       // layer count\n};\n\nrenderer.drawMultiSprite(playerMultiSprite, 100, 100);\n

    "},{"location":"api_reference/graphics/renderer/#void-drawmultispriteconst-multisprite-sprite-int-x-int-y-float-scalex-float-scaley","title":"void drawMultiSprite(const MultiSprite& sprite, int x, int y, float scaleX, float scaleY)","text":"

    Draws a scaled multi-layer sprite.

    Parameters: - sprite (const MultiSprite&): Multi-layer sprite descriptor - x (int): Top-left X coordinate - y (int): Top-left Y coordinate - scaleX (float): Horizontal scaling factor - scaleY (float): Vertical scaling factor

    "},{"location":"api_reference/graphics/renderer/#void-drawtilemapconst-tilemap-map-int-originx-int-originy-color-color","title":"void drawTileMap(const TileMap& map, int originX, int originY, Color color)","text":"

    Draws a 1bpp tilemap.

    Parameters: - map (const TileMap&): Tilemap descriptor (indices, 1bpp tiles, dimensions) - originX (int): X coordinate of the top-left corner - originY (int): Y coordinate of the top-left corner - color (Color): Color used for all tiles in the map

    "},{"location":"api_reference/graphics/renderer/#void-drawtilemapconst-tilemap2bpp-map-int-originx-int-originy","title":"void drawTileMap(const TileMap2bpp& map, int originX, int originY)","text":"

    Draws a 2bpp tilemap. Available when PIXELROOT32_ENABLE_2BPP_SPRITES is defined.

    Parameters: - map (const TileMap2bpp&): Tilemap descriptor (indices, 2bpp tiles, dimensions) - originX (int): X coordinate - originY (int): Y coordinate

    "},{"location":"api_reference/graphics/renderer/#void-drawtilemapconst-tilemap4bpp-map-int-originx-int-originy","title":"void drawTileMap(const TileMap4bpp& map, int originX, int originY)","text":"

    Draws a 4bpp tilemap. Available when PIXELROOT32_ENABLE_4BPP_SPRITES is defined.

    Parameters: - map (const TileMap4bpp&): Tilemap descriptor (indices, 4bpp tiles, dimensions) - originX (int): X coordinate - originY (int): Y coordinate

    Performance Notes: - Very efficient for rendering large backgrounds - Only visible tiles are drawn (viewport culling) - Use tilemaps instead of individual sprites for backgrounds

    Example:

    static const uint8_t levelIndices[] = {\n    0, 1, 2, 3,\n    4, 5, 6, 7,\n    // ... more rows\n};\n\nstatic const TileMap levelMap = {\n    levelIndices,\n    16,        // width in tiles\n    16,        // height in tiles\n    tileSprites, // tile sprite array\n    8,         // tile width\n    8,         // tile height\n    16         // tile count\n};\n\nrenderer.drawTileMap(levelMap, 0, 0, Color::White);\n

    "},{"location":"api_reference/graphics/renderer/#void-setdisplayoffsetint-x-int-y","title":"void setDisplayOffset(int x, int y)","text":"

    Sets a global offset for all drawing operations. Useful for camera/parallax effects.

    Parameters: - x (int): X offset in pixels - y (int): Y offset in pixels

    Notes: - All subsequent drawing operations are offset by this amount - Useful for camera scrolling and parallax effects - Reset to (0, 0) to disable offset

    Example:

    // Camera scrolling\ncamera.setPosition(playerX - 64, playerY - 64);\nrenderer.setDisplayOffset(-camera.getX(), -camera.getY());\nrenderer.drawTileMap(background, 0, 0, Color::White);\n

    "},{"location":"api_reference/graphics/renderer/#void-setdisplaysizeint-w-int-h","title":"void setDisplaySize(int w, int h)","text":"

    Sets the logical display size.

    Parameters: - w (int): Width in pixels - h (int): Height in pixels

    Notes: - Typically set via DisplayConfig during construction - Use this to change display size at runtime if needed

    "},{"location":"api_reference/graphics/renderer/#int-getwidth-const","title":"int getWidth() const","text":"

    Gets the display width.

    Returns: - int: Display width in pixels

    "},{"location":"api_reference/graphics/renderer/#int-getheight-const","title":"int getHeight() const","text":"

    Gets the display height.

    Returns: - int: Display height in pixels

    "},{"location":"api_reference/graphics/renderer/#int-getxoffset-const","title":"int getXOffset() const","text":"

    Gets the current X display offset.

    Returns: - int: X offset in pixels

    "},{"location":"api_reference/graphics/renderer/#int-getyoffset-const","title":"int getYOffset() const","text":"

    Gets the current Y display offset.

    Returns: - int: Y offset in pixels

    "},{"location":"api_reference/graphics/renderer/#void-setcontrastuint8_t-level","title":"void setContrast(uint8_t level)","text":"

    Sets the display contrast (brightness).

    Parameters: - level (uint8_t): Contrast level (0-255)

    Notes: - Platform-specific: may not be supported on all displays - Higher values = brighter display

    "},{"location":"api_reference/graphics/renderer/#void-setfontconst-uint8_t-font","title":"void setFont(const uint8_t* font)","text":"

    Sets the font for text rendering.

    Parameters: - font (const uint8_t*): Pointer to the font data

    Notes: - Sets the default font for drawText() calls without font parameter - Use font constants like FONT_5X7 from Font.h

    "},{"location":"api_reference/graphics/renderer/#usage-example","title":"Usage Example","text":"
    #include \"graphics/Renderer.h\"\n#include \"graphics/DisplayConfig.h\"\n\nvoid draw(Renderer& renderer) override {\n    renderer.beginFrame();\n\n    // Draw background\n    renderer.drawFilledRectangle(0, 0, 128, 128, Color::Black);\n\n    // Draw sprites\n    renderer.drawSprite(playerSprite, playerX, playerY, Color::White);\n    renderer.drawSprite(enemySprite, enemyX, enemyY, Color::Red);\n\n    // Draw UI\n    renderer.drawText(\"Score: 100\", 10, 10, Color::White, 1);\n    renderer.drawTextCentered(\"Game Over\", 64, Color::Yellow, 2);\n\n    renderer.endFrame();\n}\n
    "},{"location":"api_reference/graphics/renderer/#performance-considerations","title":"Performance Considerations","text":"
    • Integer-only math: All operations use integer arithmetic for ESP32 efficiency
    • Sprite storage: Store sprite data in flash (const/constexpr) for best performance
    • Batch operations: Group similar draw calls together
    • Tilemaps: Dibuja un mapa de tiles completo. Implementa viewport culling autom\u00e1tico y cach\u00e9 de paleta para m\u00e1ximo rendimiento.
    • Sprites 2bpp/4bpp: Optimizado para ESP32 (IRAM + acceso de 16 bits).
    "},{"location":"api_reference/graphics/renderer/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Sprite data should be in flash, not RAM
    • Frame rate: Limit draw calls per frame for consistent FPS
    • Display offset: Use for scrolling instead of redrawing everything
    "},{"location":"api_reference/graphics/renderer/#see-also","title":"See Also","text":"
    • Sprite - Sprite structure
    • MultiSprite - Multi-layer sprites
    • TileMap - Tilemap structure
    • Color - Color constants
    • DisplayConfig - Display configuration
    • Camera2D - Camera for scrolling
    • Manual - Basic Rendering
    • Manual - Sprites and Animation
    • API Overview
    "},{"location":"api_reference/graphics/sprite/","title":"Sprite","text":"

    Low-level bitmap descriptor and multi-layer composition for retro rendering.

    "},{"location":"api_reference/graphics/sprite/#description","title":"Description","text":"

    Sprites are the fundamental graphics primitive in PixelRoot32. The engine supports multiple sprite formats:

    • 1bpp (Standard): Monochrome sprites, most memory-efficient
    • 2bpp (Experimental): 4 colors per sprite
    • 4bpp (Experimental): 16 colors per sprite
    • MultiSprite: Multi-layer 1bpp sprites for multi-color effects
    "},{"location":"api_reference/graphics/sprite/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    struct Sprite {\n        // ...\n    };\n\n    struct MultiSprite {\n        // ...\n    };\n\n    struct SpriteLayer {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/graphics/sprite/#sprite-structure-1bpp","title":"Sprite Structure (1bpp)","text":"

    Compact sprite descriptor for monochrome bitmapped sprites.

    "},{"location":"api_reference/graphics/sprite/#members","title":"Members","text":"
    • const uint16_t* data: Pointer to packed row data (size = height)
    • uint8_t width: Sprite width in pixels (\u2264 16)
    • uint8_t height: Sprite height in pixels
    "},{"location":"api_reference/graphics/sprite/#data-format","title":"Data Format","text":"

    Sprites are stored as an array of 16-bit rows. Each row packs horizontal pixels into bits:

    • Bit 0: Leftmost pixel of the row
    • Bit (width - 1): Rightmost pixel of the row
    • Bit value 1: Pixel on (colored)
    • Bit value 0: Pixel off (transparent/background)

    Only the lowest width bits of each row are used.

    "},{"location":"api_reference/graphics/sprite/#example","title":"Example","text":"
    // 8x8 sprite (smiley face)\nstatic const uint16_t SMILEY_DATA[] = {\n    0b00111100,  // Row 0:  \u2588\u2588\u2588\u2588\n    0b01111110,  // Row 1:  \u2588\u2588\u2588\u2588\u2588\u2588\n    0b11011011,  // Row 2:  \u2588\u2588 \u2588\u2588 \u2588\u2588\n    0b11111111,  // Row 3:  \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\n    0b11011011,  // Row 4:  \u2588\u2588 \u2588\u2588 \u2588\u2588\n    0b01100110,  // Row 5:  \u2588\u2588  \u2588\u2588\n    0b01111110,  // Row 6:  \u2588\u2588\u2588\u2588\u2588\u2588\n    0b00111100   // Row 7:  \u2588\u2588\u2588\u2588\n};\n\nstatic const Sprite SMILEY_SPRITE = {\n    SMILEY_DATA,\n    8,  // width\n    8   // height\n};\n
    "},{"location":"api_reference/graphics/sprite/#spritelayer-structure","title":"SpriteLayer Structure","text":"

    Single monochrome layer used by layered sprites.

    "},{"location":"api_reference/graphics/sprite/#members_1","title":"Members","text":"
    • const uint16_t* data: Pointer to packed row data for this layer
    • Color color: Color used for \"on\" pixels in this layer
    "},{"location":"api_reference/graphics/sprite/#notes","title":"Notes","text":"
    • Each layer uses the same width/height as its owning MultiSprite
    • Layers can have different colors
    • Layers are drawn in array order
    "},{"location":"api_reference/graphics/sprite/#multisprite-structure","title":"MultiSprite Structure","text":"

    Multi-layer, multi-color sprite built from 1bpp layers.

    "},{"location":"api_reference/graphics/sprite/#members_2","title":"Members","text":"
    • uint8_t width: Sprite width in pixels (\u2264 16)
    • uint8_t height: Sprite height in pixels
    • const SpriteLayer* layers: Pointer to array of layers
    • uint8_t layerCount: Number of layers in the array
    "},{"location":"api_reference/graphics/sprite/#notes_1","title":"Notes","text":"
    • Combines several 1bpp layers with different colors
    • Layers are drawn in array order
    • Enables multi-color sprites without higher bit-depths
    • More layers = more draw calls (but still efficient)
    "},{"location":"api_reference/graphics/sprite/#example_1","title":"Example","text":"
    // Outline layer\nstatic const uint16_t OUTLINE_DATA[] = {\n    0b11111111,\n    0b10000001,\n    0b10000001,\n    0b11111111\n};\n\n// Fill layer\nstatic const uint16_t FILL_DATA[] = {\n    0b00000000,\n    0b01111110,\n    0b01111110,\n    0b00000000\n};\n\nstatic const SpriteLayer LAYERS[] = {\n    {OUTLINE_DATA, Color::Black},  // Layer 0: Black outline\n    {FILL_DATA, Color::Red}       // Layer 1: Red fill\n};\n\nstatic const MultiSprite PLAYER_MULTISPRITE = {\n    8,      // width\n    8,      // height\n    LAYERS, // layers array\n    2       // layer count\n};\n
    "},{"location":"api_reference/graphics/sprite/#sprite2bpp-structure-experimental","title":"Sprite2bpp Structure (Experimental)","text":"

    2-bit per pixel sprite (4 colors).

    Requires: PIXELROOT32_ENABLE_2BPP_SPRITES build flag

    "},{"location":"api_reference/graphics/sprite/#members_3","title":"Members","text":"
    • const uint16_t* data: Datos empaquetados (4 p\u00edxeles por cada 8 bits, alineados a 16 bits)
    • const Color* palette: Local palette (4 colors)
    • uint8_t width: Sprite width
    • uint8_t height: Sprite height
    • uint8_t paletteSize: Number of colors (typically 4)
    "},{"location":"api_reference/graphics/sprite/#notes_2","title":"Notes","text":"
    • Experimental feature
    • Uses more memory than 1bpp
    • Each pixel can be one of 4 colors from local palette
    "},{"location":"api_reference/graphics/sprite/#sprite4bpp-structure-experimental","title":"Sprite4bpp Structure (Experimental)","text":"

    4-bit per pixel sprite (16 colors).

    Requires: PIXELROOT32_ENABLE_4BPP_SPRITES build flag

    "},{"location":"api_reference/graphics/sprite/#members_4","title":"Members","text":"
    • const uint16_t* data: Datos empaquetados (2 p\u00edxeles por cada 8 bits, alineados a 16 bits)
    • const Color* palette: Local palette (16 colors)
    • uint8_t width: Sprite width
    • uint8_t height: Sprite height
    • uint8_t paletteSize: Number of colors (typically 16)
    "},{"location":"api_reference/graphics/sprite/#notes_3","title":"Notes","text":"
    • Experimental feature
    • Uses more memory than 1bpp/2bpp
    • Each pixel can be one of 16 colors from local palette
    "},{"location":"api_reference/graphics/sprite/#sprite-animation","title":"Sprite Animation","text":""},{"location":"api_reference/graphics/sprite/#spriteanimationframe-structure","title":"SpriteAnimationFrame Structure","text":"

    Frame that can reference either a Sprite or a MultiSprite.

    Members: - const Sprite* sprite: Optional pointer to a simple 1bpp sprite frame - const MultiSprite* multiSprite: Optional pointer to a layered sprite frame

    Notes: - Exactly one pointer should be non-null for a valid frame - Allows same animation system for both sprite types

    "},{"location":"api_reference/graphics/sprite/#spriteanimation-structure","title":"SpriteAnimation Structure","text":"

    Lightweight, step-based sprite animation controller.

    Members: - const SpriteAnimationFrame* frames: Pointer to immutable frame table - uint8_t frameCount: Number of frames in the table - uint8_t current: Current frame index [0, frameCount)

    Methods: - void reset(): Reset to first frame - void step(): Advance to next frame (wrapping) - const SpriteAnimationFrame& getCurrentFrame() const: Get current frame - const Sprite* getCurrentSprite() const: Get current simple sprite - const MultiSprite* getCurrentMultiSprite() const: Get current multi-sprite

    Example:

    static const SpriteAnimationFrame WALK_FRAMES[] = {\n    {&walkFrame1, nullptr},\n    {&walkFrame2, nullptr},\n    {&walkFrame3, nullptr},\n    {&walkFrame2, nullptr}  // Loop back\n};\n\nstatic SpriteAnimation walkAnimation = {\n    WALK_FRAMES,\n    4,    // frameCount\n    0     // current\n};\n\n// In update\nwalkAnimation.step();\nconst Sprite* currentSprite = walkAnimation.getCurrentSprite();\n

    "},{"location":"api_reference/graphics/sprite/#usage-examples","title":"Usage Examples","text":""},{"location":"api_reference/graphics/sprite/#creating-1bpp-sprites","title":"Creating 1bpp Sprites","text":"
    // 8x8 player sprite\nstatic const uint16_t PLAYER_DATA[] = {\n    0b00111100,\n    0b01111110,\n    0b11111111,\n    0b11011011,\n    0b11111111,\n    0b01111110,\n    0b00111100,\n    0b00011000\n};\n\nstatic const Sprite PLAYER_SPRITE = {\n    PLAYER_DATA,\n    8,  // width\n    8   // height\n};\n\n// Use in rendering\nrenderer.drawSprite(PLAYER_SPRITE, 100, 100, Color::White);\n
    "},{"location":"api_reference/graphics/sprite/#creating-multisprite","title":"Creating MultiSprite","text":"
    // Outline layer\nstatic const uint16_t OUTLINE[] = {\n    0b11111111,\n    0b10000001,\n    0b10000001,\n    0b11111111\n};\n\n// Fill layer\nstatic const uint16_t FILL[] = {\n    0b00000000,\n    0b01111110,\n    0b01111110,\n    0b00000000\n};\n\nstatic const SpriteLayer LAYERS[] = {\n    {OUTLINE, Color::Black},\n    {FILL, Color::Red}\n};\n\nstatic const MultiSprite ENEMY_SPRITE = {\n    8,      // width\n    8,      // height\n    LAYERS, // layers\n    2       // layer count\n};\n\n// Use in rendering\nrenderer.drawMultiSprite(ENEMY_SPRITE, 100, 100);\n
    "},{"location":"api_reference/graphics/sprite/#sprite-animation_1","title":"Sprite Animation","text":"
    class AnimatedActor : public pixelroot32::core::Actor {\nprivate:\n    SpriteAnimation animation;\n    unsigned long animTimer = 0;\n    unsigned long animInterval = 200;  // 200ms per frame\n\npublic:\n    void update(unsigned long deltaTime) override {\n        Actor::update(deltaTime);\n\n        animTimer += deltaTime;\n        if (animTimer >= animInterval) {\n            animTimer -= animInterval;\n            animation.step();\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        const Sprite* frame = animation.getCurrentSprite();\n        if (frame) {\n            renderer.drawSprite(*frame, \n                               static_cast<int>(x), \n                               static_cast<int>(y), \n                               Color::White);\n        }\n    }\n};\n
    "},{"location":"api_reference/graphics/sprite/#sprite-flipping","title":"Sprite Flipping","text":"

    Sprites can be flipped horizontally:

    // Draw normal\nrenderer.drawSprite(sprite, 100, 100, Color::White, false);\n\n// Draw flipped\nrenderer.drawSprite(sprite, 100, 100, Color::White, true);\n
    "},{"location":"api_reference/graphics/sprite/#performance-considerations","title":"Performance Considerations","text":"
    • 1bpp sprites: Most efficient (integer-only operations)
    • MultiSprite: Each layer is a separate draw call (still efficient)
    • 2bpp/4bpp: Experimental, uses more memory and CPU
    • Storage: Store sprite data in flash (const/constexpr) for best performance
    • Size limit: Sprites are limited to 16 pixels wide for 1bpp format
    "},{"location":"api_reference/graphics/sprite/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Store sprite data in flash, not RAM
    • Sprite size: Smaller sprites = faster drawing
    • Format choice: Use 1bpp when possible for best performance
    • MultiSprite: More layers = more draw calls (but acceptable)
    "},{"location":"api_reference/graphics/sprite/#see-also","title":"See Also","text":"
    • Renderer - Rendering system that draws sprites
    • Color - Color constants for sprites
    • Manual - Sprites and Animation
    • API Overview
    "},{"location":"api_reference/graphics/tilemap/","title":"TileMap","text":"

    Generic structure for tile-based background rendering.

    "},{"location":"api_reference/graphics/tilemap/#description","title":"Description","text":"

    TileMapGeneric<T> is a template structure for rendering tile-based backgrounds efficiently. It supports multiple bit-depths (1bpp, 2bpp, 4bpp) by using the appropriate sprite type for tiles.

    Tilemaps are ideal for large backgrounds, levels, and static environments. They support viewport culling (only visible tiles are drawn) for optimal performance.

    "},{"location":"api_reference/graphics/tilemap/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics {\n    template<typename T>\n    struct TileMapGeneric {\n        // ...\n    };\n\n    using TileMap = TileMapGeneric<Sprite>;\n    using TileMap2bpp = TileMapGeneric<Sprite2bpp>;\n    using TileMap4bpp = TileMapGeneric<Sprite4bpp>;\n}\n
    "},{"location":"api_reference/graphics/tilemap/#template-parameters","title":"Template Parameters","text":""},{"location":"api_reference/graphics/tilemap/#t","title":"T","text":"

    The sprite type used for tiles.

    Supported types: - Sprite (1bpp) - Sprite2bpp (2bpp) - Sprite4bpp (4bpp)

    "},{"location":"api_reference/graphics/tilemap/#structure","title":"Structure","text":""},{"location":"api_reference/graphics/tilemap/#uint8_t-indices","title":"uint8_t* indices","text":"

    Array of tile indices mapping to tile sprites.

    Type: uint8_t*

    Access: Read-write

    Notes: - Array size = width * height - Each value is an index into the tiles array - 0 = first tile, 1 = second tile, etc. - Should be stored in flash (const) for best performance

    Example:

    // 16x16 tilemap (256 tiles)\nstatic const uint8_t LEVEL_INDICES[] = {\n    0, 0, 0, 0, 1, 1, 1, 1, // Row 0\n    0, 2, 2, 2, 2, 2, 2, 0, // Row 1\n    // ... more rows\n};\n

    "},{"location":"api_reference/graphics/tilemap/#uint8_t-width","title":"uint8_t width","text":"

    Width of the tilemap in tiles.

    Type: uint8_t

    Access: Read-write

    Example:

    width = 16;  // 16 tiles wide\n

    "},{"location":"api_reference/graphics/tilemap/#uint8_t-height","title":"uint8_t height","text":"

    Height of the tilemap in tiles.

    Type: uint8_t

    Access: Read-write

    Example:

    height = 16;  // 16 tiles tall\n

    "},{"location":"api_reference/graphics/tilemap/#const-t-tiles","title":"const T* tiles","text":"

    Array of tile sprites of type T.

    Type: const T*

    Access: Read-only

    Notes: - Array of sprite pointers, one per unique tile - Indices reference this array - All tiles should be the same size - Should be stored in flash (const) for best performance

    Example (1bpp):

    static const Sprite TILE_SPRITES[] = {\n    EMPTY_TILE,   // Index 0\n    WALL_TILE,    // Index 1\n    FLOOR_TILE,   // Index 2\n    // ... more tiles\n};\n

    Example (2bpp):

    static const Sprite2bpp TILE_SPRITES_2BPP[] = {\n    TILE_GRASS,   // Index 0\n    TILE_DIRT,    // Index 1\n    // ... more tiles\n};\n

    "},{"location":"api_reference/graphics/tilemap/#uint8_t-tilewidth","title":"uint8_t tileWidth","text":"

    Width of each tile in pixels.

    Type: uint8_t

    Access: Read-write

    Notes: - All tiles must have the same width - Common values: 8, 16 pixels - Should match sprite width

    Example:

    tileWidth = 8;  // 8x8 tiles\n

    "},{"location":"api_reference/graphics/tilemap/#uint8_t-tileheight","title":"uint8_t tileHeight","text":"

    Height of each tile in pixels.

    Type: uint8_t

    Access: Read-write

    Notes: - All tiles must have the same height - Common values: 8, 16 pixels - Should match sprite height

    Example:

    tileHeight = 8;  // 8x8 tiles\n

    "},{"location":"api_reference/graphics/tilemap/#uint16_t-tilecount","title":"uint16_t tileCount","text":"

    Number of unique tiles in the tiles array.

    Type: uint16_t

    Access: Read-write

    Notes: - Must match the size of the tiles array - Indices must be < tileCount

    Example:

    tileCount = 16;  // 16 unique tiles\n

    "},{"location":"api_reference/graphics/tilemap/#creating-tilemaps","title":"Creating Tilemaps","text":""},{"location":"api_reference/graphics/tilemap/#step-1-create-tile-sprites","title":"Step 1: Create Tile Sprites","text":"
    // Empty tile (index 0)\nstatic const uint16_t EMPTY_DATA[] = {\n    0b00000000,\n    0b00000000,\n    0b00000000,\n    0b00000000,\n    0b00000000,\n    0b00000000,\n    0b00000000,\n    0b00000000\n};\n\n// Wall tile (index 1)\nstatic const uint16_t WALL_DATA[] = {\n    0b11111111,\n    0b10000001,\n    0b10000001,\n    0b10000001,\n    0b10000001,\n    0b10000001,\n    0b10000001,\n    0b11111111\n};\n\n// Floor tile (index 2)\nstatic const uint16_t FLOOR_DATA[] = {\n    0b00000000,\n    0b01111110,\n    0b01111110,\n    0b01111110,\n    0b01111110,\n    0b01111110,\n    0b01111110,\n    0b00000000\n};\n\nstatic const Sprite TILE_SPRITES[] = {\n    {EMPTY_DATA, 8, 8},  // Index 0\n    {WALL_DATA, 8, 8},   // Index 1\n    {FLOOR_DATA, 8, 8}   // Index 2\n};\n
    "},{"location":"api_reference/graphics/tilemap/#step-2-create-index-array","title":"Step 2: Create Index Array","text":"
    // 16x16 level (256 tiles)\n// 0 = empty, 1 = wall, 2 = floor\nstatic const uint8_t LEVEL_INDICES[] = {\n    // Row 0: Top wall\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n    // Row 1: Walls on sides\n    1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,\n    // Row 2\n    1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,\n    // ... more rows\n    // Row 15: Bottom wall\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1\n};\n
    "},{"location":"api_reference/graphics/tilemap/#step-3-create-tilemap-structure","title":"Step 3: Create TileMap Structure","text":"
    static const TileMap LEVEL_MAP = {\n    const_cast<uint8_t*>(LEVEL_INDICES),  // indices (non-const for struct)\n    16,        // width in tiles\n    16,        // height in tiles\n    TILE_SPRITES, // tile sprites array\n    8,         // tile width\n    8,         // tile height\n    3          // tile count\n};\n
    "},{"location":"api_reference/graphics/tilemap/#rendering-tilemaps","title":"Rendering Tilemaps","text":"

    Use Renderer::drawTileMap():

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Draw tilemap at origin (0, 0)\n    renderer.drawTileMap(LEVEL_MAP, 0, 0, Color::White);\n\n    // With camera offset\n    camera.apply(renderer);\n    renderer.drawTileMap(LEVEL_MAP, 0, 0, Color::White);\n}\n
    "},{"location":"api_reference/graphics/tilemap/#viewport-culling","title":"Viewport Culling","text":"

    Tilemaps automatically cull tiles outside the viewport:

    • Only visible tiles are drawn
    • Very efficient for large levels
    • Works with camera scrolling

    Example:

    // Large level (256x256 tiles)\n// Only tiles visible on screen are drawn\ncamera.apply(renderer);\nrenderer.drawTileMap(LARGE_LEVEL_MAP, 0, 0, Color::White);\n

    "},{"location":"api_reference/graphics/tilemap/#collision-detection-with-tilemaps","title":"Collision Detection with Tilemaps","text":"

    Check tile at world position:

    bool isSolidTile(int worldX, int worldY, const TileMap& map) {\n    int tileX = worldX / map.tileWidth;\n    int tileY = worldY / map.tileHeight;\n\n    if (tileX < 0 || tileX >= map.width || \n        tileY < 0 || tileY >= map.height) {\n        return true;  // Outside map = solid\n    }\n\n    int index = tileY * map.width + tileX;\n    uint8_t tileIndex = map.indices[index];\n\n    // Check if tile is solid (e.g., wall = index 1)\n    return (tileIndex == 1);\n}\n
    "},{"location":"api_reference/graphics/tilemap/#usage-example","title":"Usage Example","text":"
    #include \"graphics/TileMap.h\"\n#include \"graphics/Renderer.h\"\n\nclass LevelScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::TileMap levelMap;\n    pixelroot32::graphics::Camera2D camera;\n\npublic:\n    void init() override {\n        // Level map is already defined (see above)\n        // Create camera\n        auto& renderer = engine.getRenderer();\n        camera = pixelroot32::graphics::Camera2D(\n            renderer.getWidth(),\n            renderer.getHeight()\n        );\n\n        // Set level boundaries\n        int levelWidth = levelMap.width * levelMap.tileWidth;\n        int levelHeight = levelMap.height * levelMap.tileHeight;\n        camera.setBounds(0.0f, levelWidth - renderer.getWidth());\n        camera.setVerticalBounds(0.0f, levelHeight - renderer.getHeight());\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Camera follows player\n        if (player) {\n            camera.followTarget(player->x, player->y);\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Apply camera\n        camera.apply(renderer);\n\n        // Draw tilemap (viewport culling automatic)\n        renderer.drawTileMap(levelMap, 0, 0, Color::White);\n\n        // Draw entities\n        Scene::draw(renderer);\n\n        // Reset for UI\n        renderer.setDisplayOffset(0, 0);\n    }\n\n    bool checkTileCollision(float x, float y) {\n        int tileX = static_cast<int>(x) / levelMap.tileWidth;\n        int tileY = static_cast<int>(y) / levelMap.tileHeight;\n\n        if (tileX < 0 || tileX >= levelMap.width || \n            tileY < 0 || tileY >= levelMap.height) {\n            return true;  // Outside = solid\n        }\n\n        int index = tileY * levelMap.width + tileX;\n        uint8_t tile = levelMap.indices[index];\n        return (tile == 1);  // Wall tile\n    }\n};\n
    "},{"location":"api_reference/graphics/tilemap/#performance-considerations","title":"Performance Considerations","text":"
    • Viewport culling: Only visible tiles are drawn (automatic)
    • Tile reuse: Reuse tile sprites across the map
    • Index storage: Compact uint8_t indices (1 byte per tile)
    • Memory: Store indices and tiles in flash (const) for best performance
    • Tile size: Smaller tiles = more tiles to draw, but more detail
    "},{"location":"api_reference/graphics/tilemap/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Store tilemap data in flash, not RAM
    • Map size: Large maps use more flash memory
    • Tile count: Limit unique tiles to save memory
    • Culling: Viewport culling is essential for large levels
    "},{"location":"api_reference/graphics/tilemap/#see-also","title":"See Also","text":"
    • Renderer - Rendering system that draws tilemaps
    • Sprite - Sprite structure used for tiles
    • Camera2D - Camera for scrolling tilemaps
    • Manual - Tilemaps
    • API Overview
    "},{"location":"api_reference/physics/collision_system/","title":"CollisionSystem","text":"

    Manages collision detection between entities.

    "},{"location":"api_reference/physics/collision_system/#description","title":"Description","text":"

    CollisionSystem iterates through registered entities, checks if they are Actors, and performs AABB (Axis-Aligned Bounding Box) collision checks based on their collision layers and masks.

    The system automatically filters collisions using layers and masks, avoiding unnecessary checks. When a collision is detected, it triggers the onCollision() callback on both actors.

    "},{"location":"api_reference/physics/collision_system/#namespace","title":"Namespace","text":"
    namespace pixelroot32::physics {\n    class CollisionSystem {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/physics/collision_system/#inheritance","title":"Inheritance","text":"
    • Base class: None (standalone class)
    • Used by: Scene (manages collision system instance)
    "},{"location":"api_reference/physics/collision_system/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/physics/collision_system/#void-addentityentity-e","title":"void addEntity(Entity* e)","text":"

    Adds an entity to the collision system.

    Parameters: - e (pixelroot32::core::Entity*): Pointer to the entity to add

    Returns: - void

    Notes: - Only Actor entities participate in collisions - Entities are automatically added when added to Scene - Typically not called directly (handled by Scene)

    Example:

    // Typically handled automatically by Scene\nscene->addEntity(actor);  // Automatically added to collision system\n

    "},{"location":"api_reference/physics/collision_system/#void-removeentityentity-e","title":"void removeEntity(Entity* e)","text":"

    Removes an entity from the collision system.

    Parameters: - e (pixelroot32::core::Entity*): Pointer to the entity to remove

    Returns: - void

    Notes: - Entities are automatically removed when removed from Scene - Typically not called directly

    Example:

    // Typically handled automatically by Scene\nscene->removeEntity(actor);  // Automatically removed from collision system\n

    "},{"location":"api_reference/physics/collision_system/#void-update","title":"void update()","text":"

    Performs collision detection for all registered entities.

    Returns: - void

    Notes: - Called automatically by Scene::update() - Iterates through all pairs of entities - Only checks collisions between Actors with matching layers/masks - Triggers onCollision() callbacks when collisions are detected - Uses AABB (Axis-Aligned Bounding Box) collision detection

    Example:

    // Called automatically by Scene, but can be called manually:\nvoid update(unsigned long deltaTime) override {\n    Scene::update(deltaTime);  // Calls collisionSystem.update()\n\n    // Or manually:\n    collisionSystem.update();\n}\n

    "},{"location":"api_reference/physics/collision_system/#how-it-works","title":"How It Works","text":"
    1. Entity Registration: Entities added to Scene are automatically registered
    2. Layer Filtering: Only actors with matching layers/masks are checked
    3. AABB Check: Uses Rect::intersects() for collision detection
    4. Callback: Calls onCollision() on both actors when collision detected
    "},{"location":"api_reference/physics/collision_system/#collision-detection-algorithm","title":"Collision Detection Algorithm","text":"
    for each actor1 in entities:\n    if actor1 is not an Actor: continue\n    for each actor2 in entities:\n        if actor2 is not an Actor: continue\n        if actor1 == actor2: continue\n\n        // Check layers/masks\n        if (actor1->layer & actor2->mask) == 0: continue\n        if (actor2->layer & actor1->mask) == 0: continue\n\n        // Check AABB intersection\n        Rect box1 = actor1->getHitBox();\n        Rect box2 = actor2->getHitBox();\n\n        if (box1.intersects(box2)) {\n            actor1->onCollision(actor2);\n            actor2->onCollision(actor1);\n        }\n
    "},{"location":"api_reference/physics/collision_system/#usage-example","title":"Usage Example","text":"
    #include \"physics/CollisionSystem.h\"\n#include \"core/Actor.h\"\n\nclass GameScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Create actors with collision layers\n        PlayerActor* player = new PlayerActor(64, 64);\n        player->layer = pixelroot32::physics::DefaultLayers::kPlayer;\n        player->mask = pixelroot32::physics::DefaultLayers::kEnemy | \n                       pixelroot32::physics::DefaultLayers::kObstacle;\n        addEntity(player);\n\n        EnemyActor* enemy = new EnemyActor(100, 100);\n        enemy->layer = pixelroot32::physics::DefaultLayers::kEnemy;\n        enemy->mask = pixelroot32::physics::DefaultLayers::kPlayer;\n        addEntity(enemy);\n\n        // Collision system is managed by Scene\n        // Collisions are checked automatically in Scene::update()\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Collision detection happens here automatically\n        Scene::update(deltaTime);\n    }\n};\n
    "},{"location":"api_reference/physics/collision_system/#performance-considerations","title":"Performance Considerations","text":"
    • Layer filtering: Very efficient; avoids most collision checks
    • AABB checks: Fast (simple rectangle intersection)
    • Pair checking: O(n\u00b2) complexity, but n is limited (MAX_ENTITIES = 32)
    • Update frequency: Called every frame; keep hitboxes simple
    "},{"location":"api_reference/physics/collision_system/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Entity limit: MAX_ENTITIES = 32 limits collision pairs
    • Layer efficiency: Use layers effectively to minimize checks
    • Hitbox simplicity: Keep hitboxes as simple AABB for best performance
    "},{"location":"api_reference/physics/collision_system/#see-also","title":"See Also","text":"
    • Actor - Actors that participate in collisions
    • CollisionTypes - Collision primitives and layers
    • Manual - Physics and Collisions
    • API Overview
    "},{"location":"api_reference/physics/collision_types/","title":"Collision Types","text":"

    Basic geometric types and collision layer definitions.

    "},{"location":"api_reference/physics/collision_types/#description","title":"Description","text":"

    This document describes the collision primitives (Rect, Circle, Segment) and collision layer system used by the collision detection system.

    "},{"location":"api_reference/physics/collision_types/#namespace","title":"Namespace","text":"
    namespace pixelroot32::physics {\n    // Types and functions\n}\n
    "},{"location":"api_reference/physics/collision_types/#collisionlayer-type","title":"CollisionLayer Type","text":"

    Collision layer type (bit flags).

    Type: uint16_t (typedef)

    Notes: - Uses bit flags for layer assignment - Actors can be on multiple layers (bitwise OR) - Masks define which layers an actor can collide with

    "},{"location":"api_reference/physics/collision_types/#defaultlayers-namespace","title":"DefaultLayers Namespace","text":"

    Predefined collision layers.

    "},{"location":"api_reference/physics/collision_types/#knone","title":"kNone","text":"

    No layer (0).

    Value: 0

    Usage:

    actor->layer = pixelroot32::physics::DefaultLayers::kNone;\n

    "},{"location":"api_reference/physics/collision_types/#kall","title":"kAll","text":"

    All layers (0xFFFF).

    Value: 0xFFFF

    Usage:

    actor->mask = pixelroot32::physics::DefaultLayers::kAll;  // Collide with everything\n

    "},{"location":"api_reference/physics/collision_types/#custom-layers","title":"Custom Layers","text":"

    Define custom layers using bit flags:

    namespace MyLayers {\n    const pixelroot32::physics::CollisionLayer kPlayer = 1 << 0;      // Bit 0\n    const pixelroot32::physics::CollisionLayer kEnemy = 1 << 1;       // Bit 1\n    const pixelroot32::physics::CollisionLayer kObstacle = 1 << 2;    // Bit 2\n    const pixelroot32::physics::CollisionLayer kProjectile = 1 << 3;  // Bit 3\n    const pixelroot32::physics::CollisionLayer kCollectible = 1 << 4;  // Bit 4\n}\n\n// Usage\nactor->layer = MyLayers::kPlayer;\nactor->mask = MyLayers::kEnemy | MyLayers::kObstacle;\n
    "},{"location":"api_reference/physics/collision_types/#circle-structure","title":"Circle Structure","text":"

    Represents a circle for collision detection.

    Members: - float x: Center X coordinate - float y: Center Y coordinate - float radius: Radius of the circle

    Example:

    pixelroot32::physics::Circle circle;\ncircle.x = 100.0f;\ncircle.y = 100.0f;\ncircle.radius = 10.0f;\n

    "},{"location":"api_reference/physics/collision_types/#segment-structure","title":"Segment Structure","text":"

    Represents a line segment for collision detection.

    Members: - float x1, y1: Start point coordinates - float x2, y2: End point coordinates

    Example:

    pixelroot32::physics::Segment segment;\nsegment.x1 = 0.0f;\nsegment.y1 = 0.0f;\nsegment.x2 = 100.0f;\nsegment.y2 = 100.0f;\n

    "},{"location":"api_reference/physics/collision_types/#intersection-functions","title":"Intersection Functions","text":""},{"location":"api_reference/physics/collision_types/#bool-intersectsconst-circle-a-const-circle-b","title":"bool intersects(const Circle& a, const Circle& b)","text":"

    Checks if two circles intersect.

    Parameters: - a (const Circle&): First circle - b (const Circle&): Second circle

    Returns: - bool: true if circles intersect

    Example:

    pixelroot32::physics::Circle circle1{100.0f, 100.0f, 10.0f};\npixelroot32::physics::Circle circle2{110.0f, 100.0f, 10.0f};\n\nif (pixelroot32::physics::intersects(circle1, circle2)) {\n    // Circles overlap\n}\n

    "},{"location":"api_reference/physics/collision_types/#bool-intersectsconst-circle-c-const-rect-r","title":"bool intersects(const Circle& c, const Rect& r)","text":"

    Checks if a circle and rectangle intersect.

    Parameters: - c (const Circle&): Circle - r (const Rect&): Rectangle

    Returns: - bool: true if circle and rectangle intersect

    Example:

    pixelroot32::physics::Circle circle{100.0f, 100.0f, 10.0f};\npixelroot32::core::Rect rect{95.0f, 95.0f, 20, 20};\n\nif (pixelroot32::physics::intersects(circle, rect)) {\n    // Circle overlaps rectangle\n}\n

    "},{"location":"api_reference/physics/collision_types/#bool-intersectsconst-segment-s-const-rect-r","title":"bool intersects(const Segment& s, const Rect& r)","text":"

    Checks if a line segment and rectangle intersect.

    Parameters: - s (const Segment&): Line segment - r (const Rect&): Rectangle

    Returns: - bool: true if segment and rectangle intersect

    Example:

    pixelroot32::physics::Segment segment{0.0f, 0.0f, 100.0f, 100.0f};\npixelroot32::core::Rect rect{50.0f, 50.0f, 20, 20};\n\nif (pixelroot32::physics::intersects(segment, rect)) {\n    // Segment intersects rectangle\n}\n

    "},{"location":"api_reference/physics/collision_types/#bool-sweepcirclevsrectconst-circle-start-const-circle-end-const-rect-target-float-thit","title":"bool sweepCircleVsRect(const Circle& start, const Circle& end, const Rect& target, float& tHit)","text":"

    Performs a sweep test: checks if a moving circle collides with a rectangle.

    Parameters: - start (const Circle&): Circle at start position - end (const Circle&): Circle at end position - target (const Rect&): Target rectangle - tHit (float&): Output parameter for collision time (0.0 to 1.0)

    Returns: - bool: true if collision occurs during sweep

    Notes: - Useful for fast-moving projectiles - tHit indicates when collision occurs (0.0 = start, 1.0 = end) - More expensive than simple AABB check

    Example:

    // Projectile moving from (0, 0) to (100, 100)\npixelroot32::physics::Circle start{0.0f, 0.0f, 5.0f};\npixelroot32::physics::Circle end{100.0f, 100.0f, 5.0f};\npixelroot32::core::Rect wall{50.0f, 50.0f, 20, 20};\n\nfloat tHit = 0.0f;\nif (pixelroot32::physics::sweepCircleVsRect(start, end, wall, tHit)) {\n    // Collision at time tHit\n    float hitX = 0.0f + (100.0f - 0.0f) * tHit;\n    float hitY = 0.0f + (100.0f - 0.0f) * tHit;\n}\n

    "},{"location":"api_reference/physics/collision_types/#rect-structure-from-coreentityh","title":"Rect Structure (from core/Entity.h)","text":"

    Represents a 2D rectangle for collision detection.

    Members: - float x, y: Top-left corner coordinates - int width, height: Dimensions

    Methods: - bool intersects(const Rect& other) const: Checks if rectangles overlap

    Example:

    pixelroot32::core::Rect rect1{10.0f, 20.0f, 50, 50};\npixelroot32::core::Rect rect2{30.0f, 40.0f, 50, 50};\n\nif (rect1.intersects(rect2)) {\n    // Rectangles overlap\n}\n

    "},{"location":"api_reference/physics/collision_types/#usage-examples","title":"Usage Examples","text":""},{"location":"api_reference/physics/collision_types/#layer-based-collision","title":"Layer-Based Collision","text":"
    // Define layers\nnamespace GameLayers {\n    const pixelroot32::physics::CollisionLayer kPlayer = 1 << 0;\n    const pixelroot32::physics::CollisionLayer kEnemy = 1 << 1;\n    const pixelroot32::physics::CollisionLayer kObstacle = 1 << 2;\n    const pixelroot32::physics::CollisionLayer kProjectile = 1 << 3;\n}\n\n// Player collides with enemies and obstacles\nplayer->layer = GameLayers::kPlayer;\nplayer->mask = GameLayers::kEnemy | GameLayers::kObstacle;\n\n// Enemy collides with player and obstacles\nenemy->layer = GameLayers::kEnemy;\nenemy->mask = GameLayers::kPlayer | GameLayers::kObstacle;\n\n// Projectile collides with enemies only\nprojectile->layer = GameLayers::kProjectile;\nprojectile->mask = GameLayers::kEnemy;\n
    "},{"location":"api_reference/physics/collision_types/#circle-vs-circle-collision","title":"Circle vs Circle Collision","text":"
    bool checkCollision(const Circle& a, const Circle& b) {\n    return pixelroot32::physics::intersects(a, b);\n}\n
    "},{"location":"api_reference/physics/collision_types/#sweep-test-for-fast-projectiles","title":"Sweep Test for Fast Projectiles","text":"
    class ProjectileActor : public pixelroot32::core::Actor {\nprivate:\n    float startX, startY;\n    float endX, endY;\n    float radius = 2.0f;\n\npublic:\n    bool checkWallCollision(const Rect& wall) {\n        pixelroot32::physics::Circle start{startX, startY, radius};\n        pixelroot32::physics::Circle end{endX, endY, radius};\n\n        float tHit = 0.0f;\n        if (pixelroot32::physics::sweepCircleVsRect(start, end, wall, tHit)) {\n            // Collision detected at time tHit\n            return true;\n        }\n        return false;\n    }\n};\n
    "},{"location":"api_reference/physics/collision_types/#performance-considerations","title":"Performance Considerations","text":"
    • AABB checks: Very fast (simple rectangle intersection)
    • Circle checks: Slightly slower (distance calculation)
    • Sweep tests: More expensive (use only for fast-moving objects)
    • Layer filtering: Essential for performance with many actors
    "},{"location":"api_reference/physics/collision_types/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Float math: Uses floating point; acceptable but integer math would be faster
    • Sweep tests: Use sparingly (more CPU intensive)
    • Layer efficiency: Use layers effectively to minimize checks
    "},{"location":"api_reference/physics/collision_types/#see-also","title":"See Also","text":"
    • CollisionSystem - Collision detection system
    • Actor - Actors that use collision layers
    • Manual - Physics and Collisions
    • API Overview
    "},{"location":"api_reference/ui/ui_button/","title":"UIButton","text":"

    A clickable button UI element.

    "},{"location":"api_reference/ui/ui_button/#description","title":"Description","text":"

    UIButton is a clickable button that supports both physical (keyboard/gamepad) and touch input. It can trigger a callback function when pressed and integrates with UI layouts for automatic navigation.

    Buttons support selection state (for D-pad navigation), custom styling, and text alignment.

    "},{"location":"api_reference/ui/ui_button/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIButton : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_button/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    • Inherited by: Your custom button classes (if needed)
    "},{"location":"api_reference/ui/ui_button/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_button/#uibuttonstring-t-uint8_t-index-float-x-float-y-float-w-float-h-function-callback-textalignment-textalign-center-int-fontsize-2","title":"UIButton(string t, uint8_t index, float x, float y, float w, float h, function callback, TextAlignment textAlign = CENTER, int fontSize = 2)

    Constructs a new UIButton.

    Parameters: - t (std::string): Button label text - index (uint8_t): Navigation index (for D-pad navigation in layouts) - x (float): X position - y (float): Y position - w (float): Width - h (float): Height - callback (std::function): Function to call when clicked/pressed - textAlign (TextAlignment, optional): Text alignment. Default: CENTER - fontSize (int, optional): Text size multiplier. Default: 2

    Example:

    #include \"graphics/ui/UIButton.h\"\n\nvoid onStartButtonClicked() {\n    // Start game\n    engine.setScene(&gameScene);\n}\n\nvoid onQuitButtonClicked() {\n    // Quit game\n    engine.stop();\n}\n\n// Create buttons\npixelroot32::graphics::ui::UIButton* startButton = new pixelroot32::graphics::ui::UIButton(\n    \"Start\",\n    0,  // index\n    64.0f, 64.0f,  // position\n    100.0f, 30.0f, // size\n    onStartButtonClicked,\n    pixelroot32::graphics::ui::TextAlignment::CENTER,\n    2\n);\n\npixelroot32::graphics::ui::UIButton* quitButton = new pixelroot32::graphics::ui::UIButton(\n    \"Quit\",\n    1,  // index\n    64.0f, 100.0f,\n    100.0f, 30.0f,\n    onQuitButtonClicked\n);\n

    ","text":""},{"location":"api_reference/ui/ui_button/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_button/#void-setstylecolor-textcol-color-bgcol-bool-drawbg","title":"void setStyle(Color textCol, Color bgCol, bool drawBg)

    Configures the button's visual style.

    Parameters: - textCol (Color): Color of the text - bgCol (Color): Color of the background - drawBg (bool): Whether to draw the background rectangle

    Returns: - void

    Example:

    button->setStyle(\n    pixelroot32::graphics::Color::White,  // Text color\n    pixelroot32::graphics::Color::Blue,   // Background color\n    true                                   // Draw background\n);\n

    ","text":""},{"location":"api_reference/ui/ui_button/#void-setselectedbool-selected","title":"void setSelected(bool selected)

    Sets the selection state (e.g., focused via D-pad).

    Parameters: - selected (bool): true if selected

    Returns: - void

    Notes: - Used by layouts for D-pad navigation - Selected buttons typically have different visual style - Can be set manually or automatically by layouts

    Example:

    button->setSelected(true);  // Highlight button\n

    ","text":""},{"location":"api_reference/ui/ui_button/#bool-getselected-const","title":"bool getSelected() const

    Checks if the button is currently selected.

    Returns: - bool: true if selected

    Example:

    if (button->getSelected()) {\n    // Button is focused\n}\n

    ","text":""},{"location":"api_reference/ui/ui_button/#bool-isfocusable-const-override","title":"bool isFocusable() const override

    Returns true (Buttons are always focusable).

    Returns: - bool: Always true

    ","text":""},{"location":"api_reference/ui/ui_button/#void-handleinputconst-inputmanager-input","title":"void handleInput(const InputManager& input)

    Handles input events. Checks for touch events within bounds or confirmation buttons if selected.

    Parameters: - input (const pixelroot32::input::InputManager&): The input manager instance

    Returns: - void

    Notes: - Should be called every frame in update() - Checks if button is selected and action button is pressed - Triggers callback if conditions are met

    Example:

    void update(unsigned long deltaTime) override {\n    UIElement::update(deltaTime);\n\n    auto& input = engine.getInputManager();\n    button->handleInput(input);\n}\n

    ","text":""},{"location":"api_reference/ui/ui_button/#void-press","title":"void press()

    Manually triggers the button's action.

    Returns: - void

    Notes: - Calls the button's callback function - Useful for programmatic button presses

    Example:

    button->press();  // Trigger button action\n

    ","text":""},{"location":"api_reference/ui/ui_button/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override

    Updates the button state.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    Notes: - Called automatically by Scene if isEnabled is true - Handles input and updates button state - Override to add custom update logic

    Example:

    void update(unsigned long deltaTime) override {\n    UIButton::update(deltaTime);\n\n    // Custom update logic\n    if (shouldPulse) {\n        // Animate button\n    }\n}\n

    ","text":""},{"location":"api_reference/ui/ui_button/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override

    Renders the button.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): The renderer to use

    Returns: - void

    Notes: - Called automatically by Scene if isVisible is true - Draws background (if enabled) and text - Selected state may change appearance

    Example:

    // Drawing is handled automatically\n// Override only for custom rendering\n

    ","text":""},{"location":"api_reference/ui/ui_button/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIButton.h\"\n#include \"core/Scene.h\"\n\nclass MainMenuScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIButton* startButton;\n    pixelroot32::graphics::ui::UIButton* quitButton;\n\npublic:\n    void init() override {\n        // Start button\n        startButton = new pixelroot32::graphics::ui::UIButton(\n            \"Start Game\",\n            0,  // index\n            64.0f, 50.0f,  // position (centered)\n            120.0f, 30.0f, // size\n            [this]() {\n                // Lambda callback\n                engine.setScene(&gameScene);\n            },\n            pixelroot32::graphics::ui::TextAlignment::CENTER,\n            2\n        );\n        startButton->setStyle(\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Blue,\n            true\n        );\n        addEntity(startButton);\n\n        // Quit button\n        quitButton = new pixelroot32::graphics::ui::UIButton(\n            \"Quit\",\n            1,  // index\n            64.0f, 90.0f,\n            120.0f, 30.0f,\n            [this]() {\n                // Quit game\n                engine.stop();\n            }\n        );\n        quitButton->setStyle(\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Red,\n            true\n        );\n        addEntity(quitButton);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Buttons handle input automatically\n        // Layouts handle navigation automatically\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw background\n        renderer.drawFilledRectangle(0, 0, 128, 128, Color::Black);\n\n        // Draw UI elements (buttons)\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_button/#button-navigation","title":"Button Navigation","text":"

    Buttons can be navigated with D-pad when in layouts:

    // Buttons in a vertical layout\npixelroot32::graphics::ui::UIVerticalLayout* layout = new UIVerticalLayout(64.0f, 50.0f);\nlayout->addChild(startButton);  // Index 0\nlayout->addChild(quitButton);   // Index 1\n\n// D-pad navigation is automatic\n// UP/DOWN moves selection\n// Action button (A) triggers selected button\n
    "},{"location":"api_reference/ui/ui_button/#performance-considerations","title":"Performance Considerations","text":"
    • Input handling: handleInput() is fast; safe to call every frame
    • Rendering: Simple rectangle and text; very efficient
    • Memory: Each button consumes memory (stay within MAX_ENTITIES)
    "},{"location":"api_reference/ui/ui_button/#esp32-considerations","title":"ESP32 Considerations","text":"
    • String storage: Button labels use std::string; consider memory usage
    • Callback functions: Use function pointers or lambdas (both efficient)
    "},{"location":"api_reference/ui/ui_button/#see-also","title":"See Also","text":"
    • UIElement - Base UI element class
    • UILabel - Text label
    • UILayouts - Layout containers
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_checkbox/","title":"UICheckBox","text":"

    A clickable checkbox UI element.

    "},{"location":"api_reference/ui/ui_checkbox/#description","title":"Description","text":"

    UICheckBox is a clickable checkbox that can be toggled between checked and unchecked states. It supports both physical (keyboard/gamepad) and touch input. It can trigger a callback function when its state changes and integrates with UI layouts for automatic navigation.

    "},{"location":"api_reference/ui/ui_checkbox/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UICheckBox : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_checkbox/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    "},{"location":"api_reference/ui/ui_checkbox/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_checkbox/#uicheckboxstring-label-uint8_t-index-float-x-float-y-float-w-float-h-bool-checked-false-function-callback-nullptr-int-fontsize-2","title":"UICheckBox(string label, uint8_t index, float x, float y, float w, float h, bool checked = false, function callback = nullptr, int fontSize = 2)

    Constructs a new UICheckBox.

    Parameters: - label (std::string): Checkbox label text - index (uint8_t): Navigation index (for D-pad navigation in layouts) - x (float): X position - y (float): Y position - w (float): Width - h (float): Height - checked (bool, optional): Initial checked state. Default: false - callback (std::function, optional): Function to call when the state changes. Default: nullptr - fontSize (int, optional): Text size multiplier. Default: 2

    Example:

    #include \"graphics/ui/UICheckBox.h\"\n\nvoid onCheckChanged(bool checked) {\n    if (checked) {\n        // Sound enabled\n    } else {\n        // Sound disabled\n    }\n}\n\n// Create checkbox\npixelroot32::graphics::ui::UICheckBox* soundCheckbox = new pixelroot32::graphics::ui::UICheckBox(\n    \"Enable Sound\",\n    0,  // index\n    64.0f, 64.0f,  // position\n    120.0f, 20.0f, // size\n    true,          // initial state\n    onCheckChanged,\n    1              // font size\n);\n

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_checkbox/#void-setstylecolor-textcol-color-bgcol-bool-drawbg-false","title":"void setStyle(Color textCol, Color bgCol, bool drawBg = false)

    Configures the checkbox's visual style.

    Parameters: - textCol (Color): Color of the text - bgCol (Color): Color of the background - drawBg (bool, optional): Whether to draw the background rectangle. Default: false

    Returns: - void

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#void-setcheckedbool-checked","title":"void setChecked(bool checked)

    Sets the checked state.

    Parameters: - checked (bool): True if checked

    Returns: - void

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#bool-ischecked-const","title":"bool isChecked() const

    Checks if the checkbox is currently checked.

    Returns: - bool: true if checked

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#void-toggle","title":"void toggle()

    Toggles the checkbox state and triggers the callback.

    Returns: - void

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#void-setselectedbool-selected","title":"void setSelected(bool selected)

    Sets the selection state (e.g., focused via D-pad).

    Parameters: - selected (bool): True if selected

    Returns: - void

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#bool-getselected-const","title":"bool getSelected() const

    Checks if the checkbox is currently selected.

    Returns: - bool: true if selected

    ","text":""},{"location":"api_reference/ui/ui_checkbox/#callbacks","title":"Callbacks","text":""},{"location":"api_reference/ui/ui_checkbox/#oncheckchanged","title":"onCheckChanged

    The onCheckChanged callback is a std::function<void(bool)> that is triggered whenever the checkbox state changes via setChecked() or toggle().

    checkbox->onCheckChanged = [](bool isChecked) {\n    Serial.println(isChecked ? \"Checked!\" : \"Unchecked!\");\n};\n
    ","text":""},{"location":"api_reference/ui/ui_checkbox/#navigation-layouts","title":"Navigation & Layouts","text":"

    UICheckBox is designed to work seamlessly with UILayout containers (like UIVerticalLayout).

    • Focusable: Returns true for isFocusable(), allowing it to receive focus in a layout.
    • Input Handling: When selected (focused), it listens for the button index provided in the constructor (typically the 'A' button) to toggle its state.
    • Visual Feedback: When selected, it displays a selection indicator (usually a > character) if no background is drawn, or highlights its text/border.
    "},{"location":"api_reference/ui/ui_element/","title":"UIElement","text":"

    Base class for all user interface elements.

    "},{"location":"api_reference/ui/ui_element/#description","title":"Description","text":"

    UIElement is the base class for all UI components (buttons, labels, panels, etc.). It inherits from Entity to integrate with the scene graph and automatically sets the entity type to UI_ELEMENT and render layer to 2 (UI layer).

    "},{"location":"api_reference/ui/ui_element/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    enum class UIElementType {\n        GENERIC,\n        BUTTON,\n        LABEL,\n        CHECKBOX,\n        LAYOUT\n    };\n\n    class UIElement : public pixelroot32::core::Entity {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_element/#inheritance","title":"Inheritance","text":"
    • Inherits from: pixelroot32::core::Entity
    • Inherited by: UIButton, UICheckBox, UILabel, UIPanel, and all UI layouts
    "},{"location":"api_reference/ui/ui_element/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_element/#uielementfloat-x-float-y-float-w-float-h-uielementtype-type-uielementtypegeneric","title":"UIElement(float x, float y, float w, float h, UIElementType type = UIElementType::GENERIC)","text":"

    Constructs a new UIElement.

    Parameters: - x (float): X position - y (float): Y position - w (float): Width - h (float): Height - type (UIElementType): The type of the element (default: GENERIC)

    Notes: - Entity type is automatically set to UI_ELEMENT - Render layer is automatically set to 2 (UI layer) - Position and size can be modified after construction

    Example:

    class MyUIElement : public pixelroot32::graphics::ui::UIElement {\npublic:\n    MyUIElement(float x, float y) \n        : UIElement(x, y, 100.0f, 50.0f) {}\n\n    void update(unsigned long deltaTime) override {\n        // UI logic\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // UI rendering\n    }\n};\n

    "},{"location":"api_reference/ui/ui_element/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_element/#uielementtype-gettype-const","title":"UIElementType getType() const","text":"

    Returns the type of the UI element.

    Returns: - UIElementType: The type enum value (GENERIC, BUTTON, LABEL, CHECKBOX, or LAYOUT)

    "},{"location":"api_reference/ui/ui_element/#virtual-bool-isfocusable-const","title":"virtual bool isFocusable() const","text":"

    Checks if the element is focusable/selectable. Useful for navigation logic.

    Returns: - bool: true if focusable, false otherwise (default: false)

    "},{"location":"api_reference/ui/ui_element/#void-setpositionfloat-newx-float-newy","title":"void setPosition(float newX, float newY)","text":"

    Sets the position of the element.

    Parameters: - newX (float): New X coordinate - newY (float): New Y coordinate

    Returns: - void

    Notes: - Updates element position immediately - Use for manual positioning or animations

    Example:

    uiElement->setPosition(100.0f, 50.0f);\n

    "},{"location":"api_reference/ui/ui_element/#virtual-void-getpreferredsizefloat-preferredwidth-float-preferredheight-const","title":"virtual void getPreferredSize(float& preferredWidth, float& preferredHeight) const","text":"

    Gets the preferred size of the element. Used by layouts to determine how much space the element needs.

    Parameters: - preferredWidth (float&): Output parameter for preferred width (or -1 if flexible) - preferredHeight (float&): Output parameter for preferred height (or -1 if flexible)

    Returns: - void

    Notes: - Default implementation returns current width/height - Override in derived classes for custom sizing logic - Layouts use this to arrange elements

    Example:

    void getPreferredSize(float& w, float& h) const override {\n    // Custom sizing logic\n    w = static_cast<float>(width);\n    h = static_cast<float>(height);\n}\n

    "},{"location":"api_reference/ui/ui_element/#inherited-from-entity","title":"Inherited from Entity","text":"

    UIElement inherits all properties and methods from Entity:

    • float x, y: Position
    • int width, height: Dimensions
    • bool isVisible: Visibility state
    • bool isEnabled: Enabled state
    • unsigned char renderLayer: Render layer (automatically set to 2)
    • void setVisible(bool v): Set visibility
    • void setEnabled(bool e): Set enabled state
    • virtual void update(unsigned long deltaTime): Update logic
    • virtual void draw(Renderer& renderer): Drawing logic
    "},{"location":"api_reference/ui/ui_element/#textalignment-enum","title":"TextAlignment Enum","text":"

    Text alignment options for UI elements.

    Values: - TextAlignment::LEFT: Left-aligned text - TextAlignment::CENTER: Center-aligned text - TextAlignment::RIGHT: Right-aligned text

    "},{"location":"api_reference/ui/ui_element/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIElement.h\"\n\nclass CustomUIElement : public pixelroot32::graphics::ui::UIElement {\nprivate:\n    pixelroot32::graphics::Color bgColor;\n\npublic:\n    CustomUIElement(float x, float y, float w, float h) \n        : UIElement(x, y, w, h),\n          bgColor(pixelroot32::graphics::Color::Blue) {}\n\n    void update(unsigned long deltaTime) override {\n        // Update logic (if needed)\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        if (isVisible) {\n            // Draw background\n            renderer.drawFilledRectangle(\n                static_cast<int>(x),\n                static_cast<int>(y),\n                width,\n                height,\n                bgColor\n            );\n\n            // Draw border\n            renderer.drawRectangle(\n                static_cast<int>(x),\n                static_cast<int>(y),\n                width,\n                height,\n                pixelroot32::graphics::Color::White\n            );\n        }\n    }\n\n    void getPreferredSize(float& w, float& h) const override {\n        w = static_cast<float>(width);\n        h = static_cast<float>(height);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_element/#performance-considerations","title":"Performance Considerations","text":"
    • Render layer: UI elements are on layer 2, drawn after gameplay
    • Visibility: Use isVisible = false to hide elements efficiently
    • Layout integration: Layouts automatically manage element positioning
    "},{"location":"api_reference/ui/ui_element/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Each UI element consumes memory (stay within MAX_ENTITIES)
    • Object pooling: Reuse UI elements when possible
    • Update frequency: Disable UI elements that don't need to update
    "},{"location":"api_reference/ui/ui_element/#see-also","title":"See Also","text":"
    • Entity - Base entity class
    • UIButton - Clickable button
    • UILabel - Text label
    • UILayout - Layout containers
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_label/","title":"UILabel","text":"

    A simple text label UI element.

    "},{"location":"api_reference/ui/ui_label/#description","title":"Description","text":"

    UILabel displays a string of text on the screen. It auto-calculates its bounds based on text length and font size, making it easy to create dynamic text displays.

    Labels are useful for displaying scores, status messages, menu text, and other UI information.

    "},{"location":"api_reference/ui/ui_label/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UILabel : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_label/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    • Inherited by: Your custom label classes (if needed)
    "},{"location":"api_reference/ui/ui_label/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_label/#uilabelstring-t-float-x-float-y-color-col-uint8_t-sz","title":"UILabel(string t, float x, float y, Color col, uint8_t sz)","text":"

    Constructs a new UILabel.

    Parameters: - t (std::string): Initial text - x (float): X position - y (float): Y position - col (Color): Text color - sz (uint8_t): Text size multiplier

    Example:

    #include \"graphics/ui/UILabel.h\"\n\n// Create label\npixelroot32::graphics::ui::UILabel* scoreLabel = new pixelroot32::graphics::ui::UILabel(\n    \"Score: 0\",\n    10.0f, 10.0f,  // position\n    pixelroot32::graphics::Color::White,\n    1  // size\n);\n\n// Add to scene\nscene->addEntity(scoreLabel);\n

    "},{"location":"api_reference/ui/ui_label/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_label/#void-settextconst-string-t","title":"void setText(const string& t)","text":"

    Updates the label's text. Recalculates dimensions immediately using the active font metrics to ensure precise layout.

    Parameters: - t (const std::string&): New text

    Returns: - void

    Notes: - Automatically recalculates width and height using FontManager::textWidth - Use for dynamic text (scores, timers, etc.) - Text is stored as std::string (consider memory on ESP32)

    Example:

    // Update score label\nchar scoreText[32];\nsnprintf(scoreText, sizeof(scoreText), \"Score: %d\", score);\nscoreLabel->setText(scoreText);\n

    "},{"location":"api_reference/ui/ui_label/#void-setvisiblebool-v","title":"void setVisible(bool v)","text":"

    Sets visibility.

    Parameters: - v (bool): true to show, false to hide

    Returns: - void

    Notes: - Inherited from Entity - Hides label without removing from scene

    Example:

    label->setVisible(false);  // Hide\nlabel->setVisible(true);   // Show\n

    "},{"location":"api_reference/ui/ui_label/#void-centerxint-screenwidth","title":"void centerX(int screenWidth)","text":"

    Centers the label horizontally within a given width (typically the screen width). Recalculates dimensions before positioning to ensure perfect centering.

    Parameters: - screenWidth (int): The width to center within (e.g., engine.getRenderer().getWidth())

    Returns: - void

    Example:

    label->centerX(128); // Center on a 128px screen\n

    "},{"location":"api_reference/ui/ui_label/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the label state.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    Notes: - Called automatically by Scene if isEnabled is true - Default implementation does nothing - Override to add custom update logic (animations, etc.)

    Example:

    void update(unsigned long deltaTime) override {\n    UILabel::update(deltaTime);\n\n    // Custom logic (e.g., update text based on game state)\n    if (scoreChanged) {\n        updateScoreText();\n    }\n}\n

    "},{"location":"api_reference/ui/ui_label/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Renders the label.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): The renderer to use

    Returns: - void

    Notes: - Called automatically by Scene if isVisible is true - Draws text at label position - Uses default font

    Example:

    // Drawing is handled automatically\n// Override only for custom rendering\n

    "},{"location":"api_reference/ui/ui_label/#auto-sizing","title":"Auto-Sizing","text":"

    Labels automatically calculate their size based on text:

    • Width: text.length() * (6 * size) pixels
    • Height: 8 * size pixels

    Example:

    // Label with text \"Hello\" (5 characters), size 1\n// Width: 5 * 6 * 1 = 30 pixels\n// Height: 8 * 1 = 8 pixels\n\nUILabel label(\"Hello\", 10, 10, Color::White, 1);\n// label.width = 30, label.height = 8\n

    "},{"location":"api_reference/ui/ui_label/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UILabel.h\"\n\nclass GameScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UILabel* scoreLabel;\n    pixelroot32::graphics::ui::UILabel* livesLabel;\n    int score = 0;\n    int lives = 3;\n\npublic:\n    void init() override {\n        // Score label\n        scoreLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Score: 0\",\n            10.0f, 10.0f,\n            pixelroot32::graphics::Color::White,\n            1\n        );\n        addEntity(scoreLabel);\n\n        // Lives label\n        livesLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Lives: 3\",\n            10.0f, 25.0f,\n            pixelroot32::graphics::Color::Yellow,\n            1\n        );\n        addEntity(livesLabel);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Update labels\n        char text[32];\n        snprintf(text, sizeof(text), \"Score: %d\", score);\n        scoreLabel->setText(text);\n\n        snprintf(text, sizeof(text), \"Lives: %d\", lives);\n        livesLabel->setText(text);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw game\n        Scene::draw(renderer);\n\n        // Labels are drawn automatically by Scene::draw()\n    }\n};\n
    "},{"location":"api_reference/ui/ui_label/#centered-labels","title":"Centered Labels","text":"
    // Create centered title\npixelroot32::graphics::ui::UILabel* title = new pixelroot32::graphics::ui::UILabel(\n    \"Game Title\",\n    0, 20.0f,  // X will be adjusted\n    pixelroot32::graphics::Color::Yellow,\n    2  // Large text\n);\ntitle->centerX(128);  // Center on screen\naddEntity(title);\n
    "},{"location":"api_reference/ui/ui_label/#performance-considerations","title":"Performance Considerations","text":"
    • Text updates: setText() recalculates size; avoid calling every frame if text doesn't change
    • String storage: Uses std::string; consider memory on ESP32
    • Rendering: Simple text drawing; very efficient
    • Static text: For static text, create once and don't update
    "},{"location":"api_reference/ui/ui_label/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: std::string uses heap memory; use static buffers when possible
    • Text updates: Limit frequency of text updates
    • String length: Keep text short to save memory
    "},{"location":"api_reference/ui/ui_label/#see-also","title":"See Also","text":"
    • UIElement - Base UI element class
    • UIButton - Clickable button
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layout/","title":"UILayout","text":"

    Base class for UI layout containers.

    "},{"location":"api_reference/ui/ui_layout/#description","title":"Description","text":"

    UILayout is the base class for all layout containers. Layouts organize UI elements automatically, handling positioning, spacing, and optional scrolling. Layouts are themselves UI elements that can be added to scenes.

    "},{"location":"api_reference/ui/ui_layout/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UILayout : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layout/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    • Inherited by: UIVerticalLayout, UIHorizontalLayout, UIGridLayout, UIAnchorLayout
    "},{"location":"api_reference/ui/ui_layout/#scrollbehavior-enum","title":"ScrollBehavior Enum","text":"

    Defines how scrolling behaves in layouts.

    Values: - ScrollBehavior::NONE: No scrolling allowed - ScrollBehavior::SCROLL: Scroll freely within bounds - ScrollBehavior::CLAMP: Scroll but clamp to content bounds

    "},{"location":"api_reference/ui/ui_layout/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layout/#virtual-void-addelementuielement-element-0","title":"virtual void addElement(UIElement* element) = 0","text":"

    Adds a UI element to the layout. Must be implemented by derived classes.

    Parameters: - element (UIElement*): Pointer to the element to add

    "},{"location":"api_reference/ui/ui_layout/#virtual-void-removeelementuielement-element-0","title":"virtual void removeElement(UIElement* element) = 0","text":"

    Removes a UI element from the layout. Must be implemented by derived classes.

    Parameters: - element (UIElement*): Pointer to the element to remove

    "},{"location":"api_reference/ui/ui_layout/#virtual-void-updatelayout-0","title":"virtual void updateLayout() = 0","text":"

    Recalculates positions of all elements in the layout. Must be implemented by derived classes.

    Returns: - void

    Notes: - Should be called automatically when elements are added/removed

    "},{"location":"api_reference/ui/ui_layout/#virtual-void-handleinputconst-inputmanager-input-0","title":"virtual void handleInput(const InputManager& input) = 0","text":"

    Handles input for layout navigation (scroll, selection, etc.). Must be implemented by derived classes.

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    "},{"location":"api_reference/ui/ui_layout/#void-setpaddingfloat-p","title":"void setPadding(float p)","text":"

    Sets the padding (internal spacing) of the layout.

    Parameters: - p (float): Padding value in pixels

    Returns: - void

    Notes: - Layout is automatically recalculated

    "},{"location":"api_reference/ui/ui_layout/#float-getpadding-const","title":"float getPadding() const","text":"

    Gets the current padding.

    Returns: - float: Padding value in pixels

    "},{"location":"api_reference/ui/ui_layout/#void-setspacingfloat-s","title":"void setSpacing(float s)","text":"

    Sets the spacing between elements.

    Parameters: - s (float): Spacing value in pixels

    Returns: - void

    Notes: - Layout is automatically recalculated - Default: 4.0 pixels

    "},{"location":"api_reference/ui/ui_layout/#float-getspacing-const","title":"float getSpacing() const","text":"

    Gets the current spacing.

    Returns: - float: Spacing value in pixels

    "},{"location":"api_reference/ui/ui_layout/#size_t-getelementcount-const","title":"size_t getElementCount() const","text":"

    Gets the number of elements in the layout.

    Returns: - size_t: Element count

    "},{"location":"api_reference/ui/ui_layout/#uielement-getelementsize_t-index-const","title":"UIElement* getElement(size_t index) const","text":"

    Gets the element at a specific index.

    Parameters: - index (size_t): Element index

    Returns: - UIElement*: Pointer to the element, or nullptr if index is invalid

    "},{"location":"api_reference/ui/ui_layout/#void-clearelements","title":"void clearElements()","text":"

    Clears all elements from the layout.

    Returns: - void

    Notes: - Elements are not deleted (you must manage their lifetimes) - Layout is automatically recalculated

    "},{"location":"api_reference/ui/ui_layout/#protected-members","title":"Protected Members","text":"
    • std::vector<UIElement*> elements: List of child elements
    • float padding: Internal padding
    • float spacing: Spacing between elements (default: 4.0)
    • float scrollOffset: Current scroll offset
    • bool enableScroll: Whether scrolling is enabled
    • ScrollBehavior scrollBehavior: Scroll behavior mode
    "},{"location":"api_reference/ui/ui_layout/#see-also","title":"See Also","text":"
    • UIElement - Base UI element class
    • UIVerticalLayout - Vertical layout
    • UIHorizontalLayout - Horizontal layout
    • UIGridLayout - Grid layout
    • UIAnchorLayout - Anchor layout
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/","title":"UIAnchorLayout","text":"

    Layout that positions elements at fixed anchor points on the screen.

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#description","title":"Description","text":"

    UIAnchorLayout positions UI elements at fixed anchor points (corners, center, edges) without reflow. Very efficient for HUDs, debug UI, and fixed-position elements. Positions are calculated once or when screen size changes.

    This layout is ideal for HUD elements like score, lives, health bars, and other fixed-position UI.

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIAnchorLayout : public UILayout {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#inheritance","title":"Inheritance","text":"
    • Inherits from: UILayout
    • Inherited by: Your custom anchor layouts (if needed)
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#anchor-enum","title":"Anchor Enum","text":"

    Defines anchor points for positioning UI elements.

    Values: - Anchor::TOP_LEFT: Top-left corner - Anchor::TOP_RIGHT: Top-right corner - Anchor::BOTTOM_LEFT: Bottom-left corner - Anchor::BOTTOM_RIGHT: Bottom-right corner - Anchor::CENTER: Center of screen - Anchor::TOP_CENTER: Top center - Anchor::BOTTOM_CENTER: Bottom center - Anchor::LEFT_CENTER: Left center - Anchor::RIGHT_CENTER: Right center

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/anchor_layout/#uianchorlayoutfloat-x-float-y-float-w-float-h","title":"UIAnchorLayout(float x, float y, float w, float h)","text":"

    Constructs a new UIAnchorLayout.

    Parameters: - x (float): X position of the layout container (usually 0) - y (float): Y position of the layout container (usually 0) - w (float): Width of the layout container (usually screen width) - h (float): Height of the layout container (usually screen height)

    Example:

    #include \"graphics/ui/UIAnchorLayout.h\"\n\n// Create full-screen anchor layout for HUD\nauto& renderer = engine.getRenderer();\npixelroot32::graphics::ui::UIAnchorLayout* hud = \n    new pixelroot32::graphics::ui::UIAnchorLayout(\n        0.0f, 0.0f,\n        static_cast<float>(renderer.getWidth()),\n        static_cast<float>(renderer.getHeight())\n    );\nhud->setScreenSize(\n    static_cast<float>(renderer.getWidth()),\n    static_cast<float>(renderer.getHeight())\n);\n

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-addelementuielement-element-anchor-anchor","title":"void addElement(UIElement* element, Anchor anchor)","text":"

    Adds a UI element with a specific anchor point.

    Parameters: - element (UIElement*): Pointer to the element to add - anchor (Anchor): Anchor point for positioning

    Returns: - void

    Example:

    // Score label at top-right\nhud->addElement(scoreLabel, pixelroot32::graphics::ui::Anchor::TOP_RIGHT);\n\n// Lives label at top-left\nhud->addElement(livesLabel, pixelroot32::graphics::ui::Anchor::TOP_LEFT);\n

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-addelementuielement-element","title":"void addElement(UIElement* element)","text":"

    Adds a UI element to the layout (defaults to TOP_LEFT anchor).

    Parameters: - element (UIElement*): Pointer to the element to add

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-removeelementuielement-element","title":"void removeElement(UIElement* element)","text":"

    Removes a UI element from the layout.

    Parameters: - element (UIElement*): Pointer to the element to remove

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-updatelayout","title":"void updateLayout()","text":"

    Recalculates positions of all elements based on anchors.

    Returns: - void

    Notes: - Called automatically when screen size changes - Can be called manually if needed

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-handleinputconst-inputmanager-input","title":"void handleInput(const InputManager& input)","text":"

    Handles input (no-op for anchor layout, elements handle their own input).

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    Returns: - void

    Notes: - Anchor layout doesn't handle navigation - Elements handle their own input

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the layout and child elements.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws all elements.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#void-setscreensizefloat-screenwidth-float-screenheight","title":"void setScreenSize(float screenWidth, float screenHeight)","text":"

    Sets the screen size for anchor calculations.

    Parameters: - screenWidth (float): Screen width in pixels - screenHeight (float): Screen height in pixels

    Returns: - void

    Notes: - Used to calculate anchor positions - Should match actual display size - Layout is automatically recalculated

    Example:

    auto& renderer = engine.getRenderer();\nhud->setScreenSize(\n    static_cast<float>(renderer.getWidth()),\n    static_cast<float>(renderer.getHeight())\n);\n

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#float-getscreenwidth-const","title":"float getScreenWidth() const","text":"

    Gets the screen width.

    Returns: - float: Screen width in pixels

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#float-getscreenheight-const","title":"float getScreenHeight() const","text":"

    Gets the screen height.

    Returns: - float: Screen height in pixels

    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIAnchorLayout.h\"\n\nclass GameScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIAnchorLayout* hud;\n    pixelroot32::graphics::ui::UILabel* scoreLabel;\n    pixelroot32::graphics::ui::UILabel* livesLabel;\n\npublic:\n    void init() override {\n        auto& renderer = engine.getRenderer();\n\n        // Create HUD layout\n        hud = new pixelroot32::graphics::ui::UIAnchorLayout(\n            0.0f, 0.0f,\n            static_cast<float>(renderer.getWidth()),\n            static_cast<float>(renderer.getHeight())\n        );\n        hud->setScreenSize(\n            static_cast<float>(renderer.getWidth()),\n            static_cast<float>(renderer.getHeight())\n        );\n\n        // Score label (top-right)\n        scoreLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Score: 0\",\n            0, 0,\n            Color::White,\n            1\n        );\n        hud->addElement(scoreLabel, \n                        pixelroot32::graphics::ui::Anchor::TOP_RIGHT);\n\n        // Lives label (top-left)\n        livesLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Lives: 3\",\n            0, 0,\n            Color::Yellow,\n            1\n        );\n        hud->addElement(livesLabel, \n                        pixelroot32::graphics::ui::Anchor::TOP_LEFT);\n\n        addEntity(hud);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Update labels\n        char text[32];\n        snprintf(text, sizeof(text), \"Score: %d\", score);\n        scoreLabel->setText(text);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw game\n        Scene::draw(renderer);\n\n        // HUD is drawn automatically (on layer 2)\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#anchor-positioning","title":"Anchor Positioning","text":"

    Elements are positioned based on their anchor:

    • TOP_LEFT: Element's top-left at screen top-left
    • TOP_RIGHT: Element's top-right at screen top-right
    • BOTTOM_LEFT: Element's bottom-left at screen bottom-left
    • BOTTOM_RIGHT: Element's bottom-right at screen bottom-right
    • CENTER: Element centered on screen
    • TOP_CENTER: Element centered horizontally, top-aligned
    • BOTTOM_CENTER: Element centered horizontally, bottom-aligned
    • LEFT_CENTER: Element centered vertically, left-aligned
    • RIGHT_CENTER: Element centered vertically, right-aligned
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#performance-considerations","title":"Performance Considerations","text":"
    • No reflow: Very efficient (positions calculated once)
    • Fixed positions: Ideal for HUD elements
    • Viewport independent: Elements stay in fixed screen positions
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Very efficient (no complex calculations)
    • Update frequency: Positions only recalculate when screen size changes
    "},{"location":"api_reference/ui/ui_layouts/anchor_layout/#see-also","title":"See Also","text":"
    • UILayout - Base layout class
    • UILabel - Labels for HUD
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/grid_layout/","title":"UIGridLayout","text":"

    Grid layout container for organizing elements in a matrix.

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#description","title":"Description","text":"

    UIGridLayout organizes UI elements in a fixed grid of rows and columns. It supports navigation in 4 directions (UP/DOWN/LEFT/RIGHT) and automatic positioning based on grid coordinates.

    This layout is ideal for inventories, level selection screens, galleries, and any grid-based UI arrangement.

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIGridLayout : public UILayout {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#inheritance","title":"Inheritance","text":"
    • Inherits from: UILayout
    • Inherited by: Your custom grid layouts (if needed)
    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/grid_layout/#uigridlayoutfloat-x-float-y-float-w-float-h","title":"UIGridLayout(float x, float y, float w, float h)","text":"

    Constructs a new UIGridLayout.

    Parameters: - x (float): X position of the layout container - y (float): Y position of the layout container - w (float): Width of the layout container - h (float): Height of the layout container

    Example:

    #include \"graphics/ui/UIGridLayout.h\"\n\n// Create 4x4 grid for inventory\npixelroot32::graphics::ui::UIGridLayout* inventory = \n    new pixelroot32::graphics::ui::UIGridLayout(\n        10.0f, 10.0f,  // position\n        108.0f, 108.0f // size\n    );\ninventory->setColumns(4);\n

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-addelementuielement-element","title":"void addElement(UIElement* element)","text":"

    Adds a UI element to the layout.

    Parameters: - element (UIElement*): Pointer to the element to add

    Returns: - void

    Notes: - Elements are arranged in grid order (left to right, top to bottom) - Layout is automatically recalculated - Cell size is calculated based on columns and layout size

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-removeelementuielement-element","title":"void removeElement(UIElement* element)","text":"

    Removes a UI element from the layout.

    Parameters: - element (UIElement*): Pointer to the element to remove

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-updatelayout","title":"void updateLayout()","text":"

    Recalculates positions of all elements.

    Returns: - void

    Notes: - Recalculates cell dimensions - Recalculates row count - Repositions all elements

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-handleinputconst-inputmanager-input","title":"void handleInput(const InputManager& input)","text":"

    Handles input for navigation and selection.

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    Returns: - void

    Notes: - Handles UP/DOWN/LEFT/RIGHT navigation - Manages selection state - Wraps around grid edges (if configured)

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the layout and child elements.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws the layout and its visible elements.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-setcolumnsuint8_t-cols","title":"void setColumns(uint8_t cols)","text":"

    Sets the number of columns in the grid.

    Parameters: - cols (uint8_t): Number of columns (must be > 0)

    Returns: - void

    Notes: - Layout is automatically recalculated - Row count is calculated based on element count and columns

    Example:

    inventory->setColumns(4);  // 4 columns\n

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#uint8_t-getcolumns-const","title":"uint8_t getColumns() const","text":"

    Gets the number of columns.

    Returns: - uint8_t: Number of columns

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#uint8_t-getrows-const","title":"uint8_t getRows() const","text":"

    Gets the number of rows (calculated).

    Returns: - uint8_t: Number of rows

    Notes: - Calculated as ceil(elementCount / columns)

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#int-getselectedindex-const","title":"int getSelectedIndex() const","text":"

    Gets the currently selected element index.

    Returns: - int: Selected index, or -1 if none selected

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-setselectedindexint-index","title":"void setSelectedIndex(int index)","text":"

    Sets the selected element index.

    Parameters: - index (int): Index to select (-1 to deselect)

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#uielement-getselectedelement-const","title":"UIElement* getSelectedElement() const","text":"

    Gets the selected element.

    Returns: - UIElement*: Pointer to selected element, or nullptr if none selected

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-setnavigationbuttonsuint8_t-upbutton-uint8_t-downbutton-uint8_t-leftbutton-uint8_t-rightbutton","title":"void setNavigationButtons(uint8_t upButton, uint8_t downButton, uint8_t leftButton, uint8_t rightButton)","text":"

    Sets the navigation button indices.

    Parameters: - upButton (uint8_t): Button index for UP navigation - downButton (uint8_t): Button index for DOWN navigation - leftButton (uint8_t): Button index for LEFT navigation - rightButton (uint8_t): Button index for RIGHT navigation

    Returns: - void

    Notes: - Default: UP=0, DOWN=1, LEFT=2, RIGHT=3 - Change if your input mapping differs

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#void-setbuttonstylecolor-selectedtextcol-color-selectedbgcol-color-unselectedtextcol-color-unselectedbgcol","title":"void setButtonStyle(Color selectedTextCol, Color selectedBgCol, Color unselectedTextCol, Color unselectedBgCol)","text":"

    Sets the style colors for selected and unselected buttons.

    Parameters: - selectedTextCol (Color): Text color when selected - selectedBgCol (Color): Background color when selected - unselectedTextCol (Color): Text color when not selected - unselectedBgCol (Color): Background color when not selected

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIGridLayout.h\"\n\nclass InventoryScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIGridLayout* inventory;\n\npublic:\n    void init() override {\n        // Create 4x4 inventory grid\n        inventory = new pixelroot32::graphics::ui::UIGridLayout(\n            10.0f, 10.0f,\n            108.0f, 108.0f\n        );\n        inventory->setColumns(4);\n        inventory->setSpacing(2.0f);\n        inventory->setPadding(4.0f);\n\n        // Add inventory slots\n        for (int i = 0; i < 16; i++) {\n            auto* slot = createInventorySlot(i);\n            inventory->addElement(slot);\n        }\n\n        addEntity(inventory);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/grid_layout/#see-also","title":"See Also","text":"
    • UILayout - Base layout class (abstract)
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/","title":"UIHorizontalLayout","text":"

    Horizontal layout container with scroll support.

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#description","title":"Description","text":"

    UIHorizontalLayout organizes UI elements horizontally, one next to another. It supports scrolling when content exceeds the visible viewport and handles keyboard/D-pad navigation automatically.

    This layout is ideal for toolbars, tab bars, horizontal menus, and any horizontal arrangement of UI elements.

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIHorizontalLayout : public UILayout {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#inheritance","title":"Inheritance","text":"
    • Inherits from: UILayout
    • Inherited by: Your custom horizontal layouts (if needed)
    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#uihorizontallayoutfloat-x-float-y-float-w-float-h","title":"UIHorizontalLayout(float x, float y, float w, float h)","text":"

    Constructs a new UIHorizontalLayout.

    Parameters: - x (float): X position of the layout container - y (float): Y position of the layout container - w (float): Width of the layout container (viewport width) - h (float): Height of the layout container

    Example:

    #include \"graphics/ui/UIHorizontalLayout.h\"\n\n// Create horizontal layout for toolbar\npixelroot32::graphics::ui::UIHorizontalLayout* toolbar = \n    new pixelroot32::graphics::ui::UIHorizontalLayout(\n        0.0f, 0.0f,   // position (top of screen)\n        128.0f, 20.0f // size\n    );\n

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-addelementuielement-element","title":"void addElement(UIElement* element)","text":"

    Adds a UI element to the layout.

    Parameters: - element (UIElement*): Pointer to the element to add

    Returns: - void

    Notes: - Elements are arranged horizontally, left to right - Layout is automatically recalculated - Elements are positioned based on spacing and padding

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-removeelementuielement-element","title":"void removeElement(UIElement* element)","text":"

    Removes a UI element from the layout.

    Parameters: - element (UIElement*): Pointer to the element to remove

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-updatelayout","title":"void updateLayout()","text":"

    Recalculates positions of all elements.

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-handleinputconst-inputmanager-input","title":"void handleInput(const InputManager& input)","text":"

    Handles input for navigation and scrolling.

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    Returns: - void

    Notes: - Handles LEFT/RIGHT navigation - Manages selection state - Handles scrolling if enabled

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the layout (handles smooth scrolling).

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws the layout and its visible elements.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setscrollenabledbool-enable","title":"void setScrollEnabled(bool enable)","text":"

    Enables or disables scrolling.

    Parameters: - enable (bool): true to enable scrolling

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setviewportwidthfloat-w","title":"void setViewportWidth(float w)","text":"

    Sets the viewport width (visible area).

    Parameters: - w (float): Viewport width in pixels

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#float-getscrolloffset-const","title":"float getScrollOffset() const","text":"

    Gets the current scroll offset.

    Returns: - float: Scroll offset in pixels

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setscrolloffsetfloat-offset","title":"void setScrollOffset(float offset)","text":"

    Sets the scroll offset directly.

    Parameters: - offset (float): Scroll offset in pixels

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#float-getcontentwidth-const","title":"float getContentWidth() const","text":"

    Gets the total content width.

    Returns: - float: Content width in pixels

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#int-getselectedindex-const","title":"int getSelectedIndex() const","text":"

    Gets the currently selected element index.

    Returns: - int: Selected index, or -1 if none selected

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setselectedindexint-index","title":"void setSelectedIndex(int index)","text":"

    Sets the selected element index.

    Parameters: - index (int): Index to select (-1 to deselect)

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#uielement-getselectedelement-const","title":"UIElement* getSelectedElement() const","text":"

    Gets the selected element.

    Returns: - UIElement*: Pointer to selected element, or nullptr if none selected

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setscrollspeedfloat-speed","title":"void setScrollSpeed(float speed)","text":"

    Sets the scroll speed for smooth scrolling.

    Parameters: - speed (float): Pixels per millisecond

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setnavigationbuttonsuint8_t-leftbutton-uint8_t-rightbutton","title":"void setNavigationButtons(uint8_t leftButton, uint8_t rightButton)","text":"

    Sets the navigation button indices.

    Parameters: - leftButton (uint8_t): Button index for LEFT navigation - rightButton (uint8_t): Button index for RIGHT navigation

    Returns: - void

    Notes: - Default: LEFT = 2, RIGHT = 3 - Change if your input mapping differs

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#void-setbuttonstylecolor-selectedtextcol-color-selectedbgcol-color-unselectedtextcol-color-unselectedbgcol","title":"void setButtonStyle(Color selectedTextCol, Color selectedBgCol, Color unselectedTextCol, Color unselectedBgCol)","text":"

    Sets the style colors for selected and unselected buttons.

    Parameters: - selectedTextCol (Color): Text color when selected - selectedBgCol (Color): Background color when selected - unselectedTextCol (Color): Text color when not selected - unselectedBgCol (Color): Background color when not selected

    Returns: - void

    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIHorizontalLayout.h\"\n\nclass ToolbarScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIHorizontalLayout* toolbar;\n\npublic:\n    void init() override {\n        // Create horizontal toolbar\n        toolbar = new pixelroot32::graphics::ui::UIHorizontalLayout(\n            0.0f, 0.0f,    // Top of screen\n            128.0f, 20.0f  // Full width, 20px tall\n        );\n        toolbar->setSpacing(4.0f);\n        toolbar->setPadding(2.0f);\n\n        // Add toolbar buttons\n        toolbar->addElement(newButton(\"File\", ...));\n        toolbar->addElement(newButton(\"Edit\", ...));\n        toolbar->addElement(newButton(\"View\", ...));\n\n        addEntity(toolbar);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/horizontal_layout/#see-also","title":"See Also","text":"
    • UILayout - Base layout class (abstract)
    • UIVerticalLayout - Vertical layout
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/padding_container/","title":"UIPaddingContainer","text":"

    Container that wraps a single UI element and applies padding.

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#description","title":"Description","text":"

    UIPaddingContainer adds padding/margin around a single child element without organizing multiple elements. Useful for adding spacing to individual elements or nesting layouts with custom padding.

    This container is simpler than UIPanel (no background/border) and focuses only on spacing.

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIPaddingContainer : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    • Inherited by: Your custom padding containers (if needed)
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/padding_container/#uipaddingcontainerfloat-x-float-y-float-w-float-h","title":"UIPaddingContainer(float x, float y, float w, float h)","text":"

    Constructs a new UIPaddingContainer.

    Parameters: - x (float): X position of the container - y (float): Y position of the container - w (float): Width of the container - h (float): Height of the container

    Example:

    #include \"graphics/ui/UIPaddingContainer.h\"\n\n// Create padding container\npixelroot32::graphics::ui::UIPaddingContainer* padded = \n    new pixelroot32::graphics::ui::UIPaddingContainer(\n        10.0f, 10.0f,\n        108.0f, 108.0f\n    );\npadded->setPadding(8.0f);  // 8px padding on all sides\n

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/padding_container/#void-setchilduielement-element","title":"void setChild(UIElement* element)","text":"

    Sets the child element.

    Parameters: - element (UIElement*): Pointer to the UI element to wrap

    Returns: - void

    Notes: - Child element is positioned with padding applied - Can wrap any UI element (button, label, layout, etc.)

    Example:

    padded->setChild(button);\n

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#uielement-getchild-const","title":"UIElement* getChild() const","text":"

    Gets the child element.

    Returns: - UIElement*: Pointer to the child element, or nullptr if none set

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#void-setpaddingfloat-p","title":"void setPadding(float p)","text":"

    Sets uniform padding on all sides.

    Parameters: - p (float): Padding value in pixels

    Returns: - void

    Notes: - Applies same padding to all sides - Child position is automatically updated

    Example:

    padded->setPadding(10.0f);  // 10px padding all around\n

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#void-setpaddingfloat-left-float-right-float-top-float-bottom","title":"void setPadding(float left, float right, float top, float bottom)","text":"

    Sets asymmetric padding.

    Parameters: - left (float): Left padding in pixels - right (float): Right padding in pixels - top (float): Top padding in pixels - bottom (float): Bottom padding in pixels

    Returns: - void

    Example:

    padded->setPadding(10.0f, 5.0f, 8.0f, 12.0f);  // Different padding per side\n

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#float-getpaddingleft-const","title":"float getPaddingLeft() const","text":"

    Gets the left padding.

    Returns: - float: Left padding in pixels

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#float-getpaddingright-const","title":"float getPaddingRight() const","text":"

    Gets the right padding.

    Returns: - float: Right padding in pixels

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#float-getpaddingtop-const","title":"float getPaddingTop() const","text":"

    Gets the top padding.

    Returns: - float: Top padding in pixels

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#float-getpaddingbottom-const","title":"float getPaddingBottom() const","text":"

    Gets the bottom padding.

    Returns: - float: Bottom padding in pixels

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#void-setpositionfloat-newx-float-newy","title":"void setPosition(float newX, float newY)","text":"

    Sets the position of the container. Also updates the child element's position.

    Parameters: - newX (float): New X coordinate - newY (float): New Y coordinate

    Returns: - void

    Notes: - Child element position is updated automatically with padding applied

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the container and child element.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    Notes: - Updates child element if set - Called automatically by Scene

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws the child element.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    Notes: - Only draws child element (no background/border) - Child is drawn at padded position

    "},{"location":"api_reference/ui/ui_layouts/padding_container/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIPaddingContainer.h\"\n\nclass MenuScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Create button\n        auto* button = new UIButton(\"Start\", 0, 0, 0, 100.0f, 30.0f, \n                                    [this]() { startGame(); });\n\n        // Wrap button with padding\n        auto* paddedButton = new pixelroot32::graphics::ui::UIPaddingContainer(\n            64.0f, 50.0f,  // position\n            120.0f, 50.0f  // size (button + padding)\n        );\n        paddedButton->setPadding(10.0f);  // 10px padding\n        paddedButton->setChild(button);\n\n        addEntity(paddedButton);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#nesting-with-layouts","title":"Nesting with Layouts","text":"
    // Create layout\nauto* layout = new UIVerticalLayout(10, 10, 108, 108);\n\n// Wrap layout with padding\nauto* paddedLayout = new UIPaddingContainer(0, 0, 128, 128);\npaddedLayout->setPadding(10.0f, 10.0f, 20.0f, 20.0f);  // Asymmetric\npaddedLayout->setChild(layout);\n
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#performance-considerations","title":"Performance Considerations","text":"
    • Rendering: Very efficient (just draws child)
    • Position calculation: Fast (simple addition)
    • Memory: Minimal overhead
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Very lightweight
    • Update frequency: Position only recalculates when padding/position changes
    "},{"location":"api_reference/ui/ui_layouts/padding_container/#see-also","title":"See Also","text":"
    • UIElement - Base UI element class
    • UIPanel - Panel with background and border
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/panel/","title":"UIPanel","text":"

    Visual container that draws a background and border around a child element.

    "},{"location":"api_reference/ui/ui_layouts/panel/#description","title":"Description","text":"

    UIPanel provides a retro-style window/panel appearance with a background color and border. Typically contains a UILayout or other UI elements. Useful for dialogs, menus, and information panels.

    The panel wraps a single child element and draws a background rectangle and border around it.

    "},{"location":"api_reference/ui/ui_layouts/panel/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIPanel : public UIElement {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/panel/#inheritance","title":"Inheritance","text":"
    • Inherits from: UIElement
    • Inherited by: Your custom panel classes (if needed)
    "},{"location":"api_reference/ui/ui_layouts/panel/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/panel/#uipanelfloat-x-float-y-float-w-float-h","title":"UIPanel(float x, float y, float w, float h)","text":"

    Constructs a new UIPanel.

    Parameters: - x (float): X position of the panel - y (float): Y position of the panel - w (float): Width of the panel - h (float): Height of the panel

    Example:

    #include \"graphics/ui/UIPanel.h\"\n\n// Create dialog panel\npixelroot32::graphics::ui::UIPanel* dialog = \n    new pixelroot32::graphics::ui::UIPanel(\n        20.0f, 30.0f,  // position\n        88.0f, 68.0f   // size\n    );\n

    "},{"location":"api_reference/ui/ui_layouts/panel/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/panel/#void-setchilduielement-element","title":"void setChild(UIElement* element)","text":"

    Sets the child element.

    Parameters: - element (UIElement*): Pointer to the UI element to wrap (typically a UILayout)

    Returns: - void

    Notes: - Child element is positioned inside the panel (with padding) - Typically a layout (VerticalLayout, etc.)

    Example:

    // Create panel\nauto* panel = new UIPanel(20, 30, 88, 68);\n\n// Create layout for panel content\nauto* layout = new UIVerticalLayout(0, 0, 80, 60);\nlayout->addElement(button1);\nlayout->addElement(button2);\n\n// Set layout as panel child\npanel->setChild(layout);\n

    "},{"location":"api_reference/ui/ui_layouts/panel/#uielement-getchild-const","title":"UIElement* getChild() const","text":"

    Gets the child element.

    Returns: - UIElement*: Pointer to the child element, or nullptr if none set

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-setbackgroundcolorcolor-color","title":"void setBackgroundColor(Color color)","text":"

    Sets the background color.

    Parameters: - color (Color): Background color

    Returns: - void

    Example:

    panel->setBackgroundColor(Color::Blue);\n

    "},{"location":"api_reference/ui/ui_layouts/panel/#color-getbackgroundcolor-const","title":"Color getBackgroundColor() const","text":"

    Gets the background color.

    Returns: - Color: Background color

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-setbordercolorcolor-color","title":"void setBorderColor(Color color)","text":"

    Sets the border color.

    Parameters: - color (Color): Border color

    Returns: - void

    Example:

    panel->setBorderColor(Color::White);\n

    "},{"location":"api_reference/ui/ui_layouts/panel/#color-getbordercolor-const","title":"Color getBorderColor() const","text":"

    Gets the border color.

    Returns: - Color: Border color

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-setborderwidthuint8_t-width","title":"void setBorderWidth(uint8_t width)","text":"

    Sets the border width.

    Parameters: - width (uint8_t): Border width in pixels

    Returns: - void

    Notes: - Default: 1 pixel - Higher values = thicker border

    Example:

    panel->setBorderWidth(2);  // 2 pixel border\n

    "},{"location":"api_reference/ui/ui_layouts/panel/#uint8_t-getborderwidth-const","title":"uint8_t getBorderWidth() const","text":"

    Gets the border width.

    Returns: - uint8_t: Border width in pixels

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-setpositionfloat-newx-float-newy","title":"void setPosition(float newX, float newY)","text":"

    Sets the position of the panel. Also updates the child element's position.

    Parameters: - newX (float): New X coordinate - newY (float): New Y coordinate

    Returns: - void

    Notes: - Child element position is updated automatically

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the panel and child element.

    Parameters: - deltaTime (unsigned long): Time elapsed in milliseconds

    Returns: - void

    Notes: - Updates child element if set - Called automatically by Scene

    "},{"location":"api_reference/ui/ui_layouts/panel/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws the panel (background, border) and child element.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    Notes: - Draws background rectangle - Draws border rectangle - Draws child element if set

    "},{"location":"api_reference/ui/ui_layouts/panel/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIPanel.h\"\n#include \"graphics/ui/UIVerticalLayout.h\"\n\nclass DialogScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIPanel* dialog;\n\npublic:\n    void init() override {\n        // Create dialog panel\n        dialog = new pixelroot32::graphics::ui::UIPanel(\n            20.0f, 30.0f,  // position\n            88.0f, 68.0f   // size\n        );\n        dialog->setBackgroundColor(Color::Navy);\n        dialog->setBorderColor(Color::White);\n        dialog->setBorderWidth(2);\n\n        // Create layout for dialog content\n        auto* layout = new pixelroot32::graphics::ui::UIVerticalLayout(\n            4.0f, 4.0f,  // Position inside panel\n            80.0f, 60.0f // Size inside panel\n        );\n        layout->setSpacing(8.0f);\n\n        // Add buttons\n        auto* okButton = new UIButton(\"OK\", 0, 0, 0, 70.0f, 20.0f, \n                                     [this]() { closeDialog(); });\n        auto* cancelButton = new UIButton(\"Cancel\", 1, 0, 0, 70.0f, 20.0f,\n                                         [this]() { closeDialog(); });\n\n        layout->addElement(okButton);\n        layout->addElement(cancelButton);\n\n        // Set layout as panel child\n        dialog->setChild(layout);\n\n        addEntity(dialog);\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/panel/#performance-considerations","title":"Performance Considerations","text":"
    • Rendering: Simple rectangles; very efficient
    • Child updates: Child element updates are fast
    • Memory: Small overhead (just colors and border width)
    "},{"location":"api_reference/ui/ui_layouts/panel/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Memory: Panel is lightweight
    • Rendering: Two rectangles (background + border); minimal overhead
    "},{"location":"api_reference/ui/ui_layouts/panel/#see-also","title":"See Also","text":"
    • UIElement - Base UI element class
    • UILayouts - Layout containers to use inside panels
    • Manual - User Interface
    • API Overview
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/","title":"UIVerticalLayout","text":"

    Vertical layout container with scroll support.

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#description","title":"Description","text":"

    UIVerticalLayout organizes UI elements vertically, one below another. It supports scrolling when content exceeds the visible viewport and handles keyboard/D-pad navigation automatically.

    This layout is ideal for menus, lists, and any vertical arrangement of UI elements.

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#namespace","title":"Namespace","text":"
    namespace pixelroot32::graphics::ui {\n    class UIVerticalLayout : public UILayout {\n        // ...\n    };\n}\n
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#inheritance","title":"Inheritance","text":"
    • Inherits from: UILayout
    • Inherited by: Your custom vertical layouts (if needed)
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#constructors","title":"Constructors","text":""},{"location":"api_reference/ui/ui_layouts/vertical_layout/#uiverticallayoutfloat-x-float-y-float-w-float-h","title":"UIVerticalLayout(float x, float y, float w, float h)","text":"

    Constructs a new UIVerticalLayout.

    Parameters: - x (float): X position of the layout container - y (float): Y position of the layout container - w (float): Width of the layout container - h (float): Height of the layout container (viewport height)

    Example:

    #include \"graphics/ui/UIVerticalLayout.h\"\n\n// Create vertical layout for menu\npixelroot32::graphics::ui::UIVerticalLayout* menuLayout = \n    new pixelroot32::graphics::ui::UIVerticalLayout(\n        20.0f, 20.0f,  // position\n        88.0f, 88.0f   // size (viewport)\n    );\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#public-methods","title":"Public Methods","text":""},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-addelementuielement-element","title":"void addElement(UIElement* element)","text":"

    Adds a UI element to the layout.

    Parameters: - element (UIElement*): Pointer to the element to add

    Returns: - void

    Notes: - Elements are arranged vertically, one below another - Layout is automatically recalculated - Elements are positioned based on spacing and padding

    Example:

    menuLayout->addElement(startButton);\nmenuLayout->addElement(quitButton);\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-removeelementuielement-element","title":"void removeElement(UIElement* element)","text":"

    Removes a UI element from the layout.

    Parameters: - element (UIElement*): Pointer to the element to remove

    Returns: - void

    Notes: - Layout is automatically recalculated - Element is not deleted (you must manage its lifetime)

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-updatelayout","title":"void updateLayout()","text":"

    Recalculates positions of all elements.

    Returns: - void

    Notes: - Called automatically when elements are added/removed - Can be called manually if needed - Recalculates all element positions and content height

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-handleinputconst-inputmanager-input","title":"void handleInput(const InputManager& input)","text":"

    Handles input for navigation and scrolling.

    Parameters: - input (const pixelroot32::input::InputManager&): Reference to the InputManager

    Returns: - void

    Notes: - Handles UP/DOWN navigation - Manages selection state - Handles scrolling if enabled - Should be called every frame in update()

    Example:

    void update(unsigned long deltaTime) override {\n    UIVerticalLayout::update(deltaTime);\n\n    auto& input = engine.getInputManager();\n    menuLayout->handleInput(input);\n}\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-updateunsigned-long-deltatime-override","title":"void update(unsigned long deltaTime) override","text":"

    Updates the layout (handles smooth scrolling).

    Parameters: - deltaTime (unsigned long): Time elapsed since last frame in milliseconds

    Returns: - void

    Notes: - Called automatically by Scene if isEnabled is true - Updates smooth scrolling animation - Updates child elements

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-drawrenderer-renderer-override","title":"void draw(Renderer& renderer) override","text":"

    Draws the layout and its visible elements.

    Parameters: - renderer (pixelroot32::graphics::Renderer&): Reference to the renderer

    Returns: - void

    Notes: - Called automatically by Scene if isVisible is true - Only draws visible elements (viewport culling) - Draws elements in order

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setscrollenabledbool-enable","title":"void setScrollEnabled(bool enable)","text":"

    Enables or disables scrolling.

    Parameters: - enable (bool): true to enable scrolling

    Returns: - void

    Notes: - When disabled, scroll offset is reset to 0 - Scrolling is useful when content exceeds viewport height

    Example:

    menuLayout->setScrollEnabled(true);  // Enable scrolling\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-enablescrollbool-enable","title":"void enableScroll(bool enable)","text":"

    Alias for setScrollEnabled().

    Parameters: - enable (bool): true to enable scrolling

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setviewportheightfloat-h","title":"void setViewportHeight(float h)","text":"

    Sets the viewport height (visible area).

    Parameters: - h (float): Viewport height in pixels

    Returns: - void

    Notes: - Layout is automatically recalculated - Use to adjust visible area

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#float-getscrolloffset-const","title":"float getScrollOffset() const","text":"

    Gets the current scroll offset.

    Returns: - float: Scroll offset in pixels

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setscrolloffsetfloat-offset","title":"void setScrollOffset(float offset)","text":"

    Sets the scroll offset directly.

    Parameters: - offset (float): Scroll offset in pixels

    Returns: - void

    Notes: - Offset is clamped to valid range automatically - Use for programmatic scrolling

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#float-getcontentheight-const","title":"float getContentHeight() const","text":"

    Gets the total content height.

    Returns: - float: Content height in pixels

    Notes: - Includes all elements plus spacing and padding - Useful for scroll calculations

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#int-getselectedindex-const","title":"int getSelectedIndex() const","text":"

    Gets the currently selected element index.

    Returns: - int: Selected index, or -1 if none selected

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setselectedindexint-index","title":"void setSelectedIndex(int index)","text":"

    Sets the selected element index.

    Parameters: - index (int): Index to select (-1 to deselect)

    Returns: - void

    Notes: - Selected element is highlighted - Selection is scrolled into view if needed

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#uielement-getselectedelement-const","title":"UIElement* getSelectedElement() const","text":"

    Gets the selected element.

    Returns: - UIElement*: Pointer to selected element, or nullptr if none selected

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setscrollspeedfloat-speed","title":"void setScrollSpeed(float speed)","text":"

    Sets the scroll speed for smooth scrolling.

    Parameters: - speed (float): Pixels per millisecond

    Returns: - void

    Notes: - Default: 0.5 pixels per millisecond - Higher values = faster scrolling

    Example:

    menuLayout->setScrollSpeed(1.0f);  // Faster scrolling\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setnavigationbuttonsuint8_t-upbutton-uint8_t-downbutton","title":"void setNavigationButtons(uint8_t upButton, uint8_t downButton)","text":"

    Sets the navigation button indices.

    Parameters: - upButton (uint8_t): Button index for UP navigation - downButton (uint8_t): Button index for DOWN navigation

    Returns: - void

    Notes: - Default: UP = 0, DOWN = 1 - Change if your input mapping differs

    Example:

    menuLayout->setNavigationButtons(0, 1);  // UP=0, DOWN=1\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#void-setbuttonstylecolor-selectedtextcol-color-selectedbgcol-color-unselectedtextcol-color-unselectedbgcol","title":"void setButtonStyle(Color selectedTextCol, Color selectedBgCol, Color unselectedTextCol, Color unselectedBgCol)","text":"

    Sets the style colors for selected and unselected buttons.

    Parameters: - selectedTextCol (Color): Text color when selected - selectedBgCol (Color): Background color when selected - unselectedTextCol (Color): Text color when not selected - unselectedBgCol (Color): Background color when not selected

    Returns: - void

    Example:

    menuLayout->setButtonStyle(\n    Color::Yellow,  // Selected text\n    Color::Blue,    // Selected background\n    Color::White,   // Unselected text\n    Color::Black    // Unselected background\n);\n

    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#usage-example","title":"Usage Example","text":"
    #include \"graphics/ui/UIVerticalLayout.h\"\n#include \"graphics/ui/UIButton.h\"\n\nclass MainMenuScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIVerticalLayout* menuLayout;\n\npublic:\n    void init() override {\n        // Create menu layout\n        menuLayout = new pixelroot32::graphics::ui::UIVerticalLayout(\n            20.0f, 20.0f,  // position\n            88.0f, 88.0f   // size\n        );\n        menuLayout->setScrollEnabled(true);\n        menuLayout->setSpacing(8.0f);\n        menuLayout->setPadding(4.0f);\n\n        // Create buttons\n        auto* startButton = new pixelroot32::graphics::ui::UIButton(\n            \"Start\",\n            0, 64.0f, 50.0f, 100.0f, 30.0f,\n            [this]() { engine.setScene(&gameScene); }\n        );\n\n        auto* quitButton = new pixelroot32::graphics::ui::UIButton(\n            \"Quit\",\n            1, 64.0f, 50.0f, 100.0f, 30.0f,\n            [this]() { engine.stop(); }\n        );\n\n        // Add buttons to layout\n        menuLayout->addElement(startButton);\n        menuLayout->addElement(quitButton);\n\n        // Add layout to scene\n        addEntity(menuLayout);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Layout handles input automatically\n        auto& input = engine.getInputManager();\n        menuLayout->handleInput(input);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(0, 0, 128, 128, Color::Black);\n        Scene::draw(renderer);  // Draws layout and buttons\n    }\n};\n
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#navigation","title":"Navigation","text":"

    The layout handles D-pad navigation automatically:

    • UP button: Moves selection up
    • DOWN button: Moves selection down
    • Action button: Triggers selected button's callback
    • Scrolling: Automatically scrolls to keep selected element visible
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#performance-considerations","title":"Performance Considerations","text":"
    • Viewport culling: Only visible elements are drawn
    • Layout recalculation: Fast (simple positioning)
    • Scrolling: Smooth scrolling is efficient
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#esp32-considerations","title":"ESP32 Considerations","text":"
    • Element count: Stay within MAX_ENTITIES limit
    • Scrolling: Smooth scrolling uses minimal CPU
    "},{"location":"api_reference/ui/ui_layouts/vertical_layout/#see-also","title":"See Also","text":"
    • UILayout - Base layout class (abstract)
    • UIButton - Buttons for menus
    • Manual - User Interface
    • API Overview
    "},{"location":"getting_started/fundamental_concepts/","title":"Fundamental Concepts","text":"

    Before you start programming, it's important to understand the basic concepts that form PixelRoot32's architecture. This section explains how the engine works at a conceptual level, without going into code details.

    "},{"location":"getting_started/fundamental_concepts/#engine-architecture","title":"Engine Architecture","text":""},{"location":"getting_started/fundamental_concepts/#engine-the-heart-of-the-engine","title":"Engine: The Heart of the Engine","text":"

    The Engine is the main class that orchestrates the entire system. Think of it as the conductor that coordinates all subsystems:

    • Renderer: Handles drawing everything on screen
    • InputManager: Reads and processes user input (buttons, keyboard)
    • AudioEngine: Generates and plays sounds and music
    • SceneManager: Manages game scenes (menus, levels, etc.)

    The Engine runs the main game loop: an infinite cycle that updates game logic and draws each frame on screen. It also calculates delta time (time elapsed between frames) so the game runs at the same speed regardless of framerate.

    "},{"location":"getting_started/fundamental_concepts/#scene-organizing-your-game","title":"Scene: Organizing Your Game","text":"

    A Scene represents a screen or level in your game. For example: - A scene for the main menu - A scene for each game level - A scene for the game over screen - A scene for the pause menu

    Each scene contains and manages a set of entities (characters, enemies, objects, etc.). The scene is responsible for: - Initializing its entities when loaded - Updating the logic of all its entities each frame - Drawing all its visible entities each frame - Managing collisions between entities that can collide

    The Engine can only have one active scene at a time, but you can easily switch between scenes (for example, go from menu to game, or from game to pause menu).

    "},{"location":"getting_started/fundamental_concepts/#entity-the-fundamental-building-blocks","title":"Entity: The Fundamental Building Blocks","text":"

    An Entity is any object in your game that has: - Position (x, y) in the world - Size (width and height) - Visibility (can be visible or not) - Active state (can be enabled or disabled) - Render layer (in what order it's drawn)

    Entities are the foundation of everything in your game: the player, enemies, projectiles, objects, UI elements\u2014everything is an entity or inherits from Entity.

    Each entity has two main methods: - update(): Called each frame to update the entity's logic (movement, animation, etc.) - draw(): Called each frame to draw the entity on screen

    "},{"location":"getting_started/fundamental_concepts/#actor-entities-that-can-collide","title":"Actor: Entities That Can Collide","text":"

    An Actor is a special entity that can participate in the collision system. In addition to everything an Entity has, an Actor has: - Collision layer: Which group it belongs to (e.g., \"player\", \"enemy\", \"projectile\") - Collision mask: Which other groups it can collide with - Hitbox: The shape used to detect collisions (usually a rectangle)

    For example, a player might be on the \"player\" layer and have a mask that allows it to collide with \"enemies\" and \"obstacles\", but not with \"other players\".

    When two actors collide, the system calls their onCollision() method so they can react (e.g., player loses health, enemy is destroyed, etc.).

    "},{"location":"getting_started/fundamental_concepts/#physicsactor-entities-with-physics","title":"PhysicsActor: Entities with Physics","text":"

    A PhysicsActor is an Actor that also has physical properties: - Velocity (vx, vy): Moves automatically according to its velocity - Gravity: Can fall automatically - Friction: Gradually loses velocity - Restitution: Bounces when it collides (like a ball)

    The PhysicsActor updates automatically each frame, applying physics and moving the entity. It can also detect collisions with world boundaries (the walls of the play area).

    "},{"location":"getting_started/fundamental_concepts/#entity-hierarchy","title":"Entity Hierarchy","text":"

    The relationship between these classes is hierarchical:

    Entity (base)\n  \u2514\u2500\u2500 Actor (can collide)\n       \u2514\u2500\u2500 PhysicsActor (has physics)\n

    This means: - Every Actor is also an Entity - Every PhysicsActor is also an Actor and an Entity - You can use Entity for simple objects that don't need collisions - You can use Actor for objects that need to detect collisions - You can use PhysicsActor for objects that need automatic physics

    "},{"location":"getting_started/fundamental_concepts/#rendering-system","title":"Rendering System","text":""},{"location":"getting_started/fundamental_concepts/#render-layers","title":"Render Layers","text":"

    To control the order in which things are drawn, PixelRoot32 uses render layers:

    • Layer 0 (Background): Backgrounds, tilemaps, background elements
    • Layer 1 (Gameplay): Characters, enemies, projectiles, game objects
    • Layer 2 (UI): Menus, HUD, text, interface elements

    Layers are drawn in order: first 0, then 1, and finally 2. This ensures the background is always behind, gameplay in the middle, and UI always visible in front.

    Each entity has a renderLayer property that indicates which layer it should be drawn on. You can change this property to move entities between layers.

    "},{"location":"getting_started/fundamental_concepts/#rendering-pipeline","title":"Rendering Pipeline","text":"

    The rendering process works like this:

    1. beginFrame(): The screen is cleared (painted black or background color)
    2. Draw entities: All visible entities are traversed, organized by layer
    3. endFrame(): The complete frame is sent to the display

    The Renderer abstracts hardware details, so the same code works on both ESP32 (TFT_eSPI) and PC (SDL2).

    "},{"location":"getting_started/fundamental_concepts/#coordinates-and-space","title":"Coordinates and Space","text":"

    PixelRoot32 uses a standard coordinate system: - Origin (0, 0): Top-left corner - X-axis: Increases to the right - Y-axis: Increases downward

    Coordinates are in pixels. If your display is 240x240, coordinates range from (0, 0) to (239, 239).

    "},{"location":"getting_started/fundamental_concepts/#lifecycle","title":"Lifecycle","text":""},{"location":"getting_started/fundamental_concepts/#initialization","title":"Initialization","text":"

    When your game starts:

    1. Configuration: Configuration objects are created (DisplayConfig, InputConfig, AudioConfig)
    2. Engine: The Engine is created with these configurations
    3. init(): engine.init() is called to initialize all subsystems
    4. Scene: The initial scene is created and configured
    5. setScene(): The scene is assigned to the Engine
    "},{"location":"getting_started/fundamental_concepts/#game-loop","title":"Game Loop","text":"

    Once initialized, the Engine enters the game loop:

    While the game is running:\n  1. Calculate deltaTime (time since last frame)\n  2. Update InputManager (read buttons/keyboard)\n  3. Update AudioEngine (advance sounds and music)\n  4. Update current scene (update all entities)\n  5. Detect collisions in the scene\n  6. Draw the scene (draw all visible entities)\n  7. Repeat\n

    This cycle runs continuously, typically at 30-60 FPS on ESP32, or faster on PC.

    "},{"location":"getting_started/fundamental_concepts/#update","title":"Update","text":"

    Each frame, all enabled entities receive a call to their update(deltaTime) method. This is where: - Entities move - Animations update - Game logic is processed - User input is read - Sound effects are played

    The deltaTime is passed in milliseconds and represents how much time has passed since the last frame. This allows movement to be framerate-independent.

    "},{"location":"getting_started/fundamental_concepts/#rendering-draw","title":"Rendering (Draw)","text":"

    After updating, all visible entities receive a call to their draw(renderer) method. This is where: - Sprites are drawn - Text is drawn - Primitives are drawn (rectangles, circles, etc.)

    The renderer is passed as a parameter so entities can draw themselves.

    "},{"location":"getting_started/fundamental_concepts/#cleanup","title":"Cleanup","text":"

    When you change scenes or end the game: - Entities from the previous scene can be cleaned up - Resources are freed - The new scene is initialized

    "},{"location":"getting_started/fundamental_concepts/#conceptual-summary","title":"Conceptual Summary","text":"

    To summarize, PixelRoot32 works like this:

    1. Engine coordinates everything and runs the game loop
    2. Scene organizes your game into screens/levels
    3. Entity is any object in your game
    4. Actor is an entity that can collide
    5. PhysicsActor is an actor with automatic physics
    6. Renderer draws everything on screen using layers
    7. Each frame updates logic and then draws

    All of this works automatically once you configure the Engine and create your scenes and entities. You don't need to worry about game loop details; you just need to implement update() and draw() in your entities.

    "},{"location":"getting_started/fundamental_concepts/#next-step","title":"Next Step","text":"

    Now that you understand the fundamental concepts, you're ready to create your first project and see these concepts in action with real code.

    See also: - What is PixelRoot32? - Why PixelRoot32? - Your First Project - Manual - Scenes and Entities

    "},{"location":"getting_started/installation/","title":"Installation","text":"

    This guide covers installing the PixelRoot32 documentation environment and preparing your development setup for ESP32 and Native (PC) targets.

    "},{"location":"getting_started/installation/#requirements","title":"Requirements","text":"
    • Python 3.11 or newer
    • Git (recommended for source management)
    • VS Code (or your preferred IDE)
    • For ESP32 targets: PlatformIO (VS Code extension) with ESP32 toolchain
    • For Native targets: a C++ build toolchain (CMake or your OS-native toolchain)
    "},{"location":"getting_started/installation/#install-documentation-tooling","title":"Install Documentation Tooling","text":"

    To build and preview this documentation locally:

    pip install mkdocs mkdocs-material mkdocs-minify-plugin mkdocs-git-revision-date-localized-plugin mike\nmkdocs serve\n

    Open http://127.0.0.1:8000 in your browser to preview.

    "},{"location":"getting_started/installation/#esp32-setup-recommended","title":"ESP32 Setup (Recommended)","text":"
    1. Install VS Code
    2. Install PlatformIO IDE extension
    3. Install ESP32 platform/toolchain via PlatformIO
    4. Clone the engine repository:
    5. https://github.com/Gperez88/PixelRoot32-Game-Engine
    6. Open the engine or example project in VS Code (PlatformIO)
    7. Build and upload to your ESP32 board

    Tip: Use boards based on ESP32-WROOM/WROVER for best compatibility. Ensure a reliable USB cable and correct serial port selection.

    "},{"location":"getting_started/installation/#native-pc-setup","title":"Native (PC) Setup","text":"
    1. Install a C++ toolchain (e.g., MSVC or MinGW on Windows)
    2. Install CMake (if the engine provides CMake build files)
    3. Clone the engine repository:
    4. https://github.com/Gperez88/PixelRoot32-Game-Engine
    5. Configure and build the native runtime:
    6. Follow the engine\u2019s native build instructions (Development \u2192 Compiling)
    "},{"location":"getting_started/installation/#verify-your-environment","title":"Verify Your Environment","text":"
    • ESP32: Build and flash a minimal sample; confirm serial output and display if applicable
    • Native: Run the executable; confirm window output and input handling
    "},{"location":"getting_started/installation/#troubleshooting","title":"Troubleshooting","text":"
    • If PlatformIO cannot find the ESP32 platform, update PlatformIO and retry
    • If native builds fail, verify compiler versions and CMake generator settings
    • Use Community \u2192 Troubleshooting for common issues and fixes
    "},{"location":"getting_started/installation/#next-steps","title":"Next Steps","text":"
    • First Project
    • Concepts
    "},{"location":"getting_started/what_is_pixelroot32/","title":"What is PixelRoot32?","text":"

    PixelRoot32 is a lightweight, modular 2D game engine written in C++ designed specifically for ESP32 microcontrollers, with a native simulation layer for PC (SDL2) that allows you to develop and debug quickly on your desktop before deploying to hardware.

    "},{"location":"getting_started/what_is_pixelroot32/#simple-definition","title":"Simple Definition","text":"

    PixelRoot32 is a game engine that lets you create retro-style 8-bit/16-bit video games directly on an ESP32 board, with the ability to develop and test on your PC before transferring code to hardware.

    "},{"location":"getting_started/what_is_pixelroot32/#key-features","title":"Key Features","text":""},{"location":"getting_started/what_is_pixelroot32/#scene-based-architecture","title":"\ud83c\udfae Scene-Based Architecture","text":"
    • Scene system inspired by Godot Engine
    • Intuitive management of levels, menus, and screens
    • Simple transitions between scenes
    "},{"location":"getting_started/what_is_pixelroot32/#optimized-rendering","title":"\ud83c\udfa8 Optimized Rendering","text":"
    • 1bpp (monochrome) sprites as the standard format
    • Support for multi-layer sprites (MultiSprite)
    • Experimental 2bpp and 4bpp formats for higher fidelity
    • Retro color palette system (NES, GameBoy, PICO-8, etc.)
    • Compact tilemaps for backgrounds and levels
    • 2D camera with dead-zone for smooth scrolling
    • Render layer system (background, gameplay, UI)
    "},{"location":"getting_started/what_is_pixelroot32/#nes-like-audio","title":"\ud83d\udd0a NES-like Audio","text":"
    • 4 audio channels (2 Pulse, 1 Triangle, 1 Noise)
    • Integrated sound effects system
    • Music player for background melodies
    • Backends for ESP32 (internal DAC or external I2S) and SDL2
    "},{"location":"getting_started/what_is_pixelroot32/#physics-and-collisions","title":"\ud83c\udfaf Physics and Collisions","text":"
    • AABB (Axis-Aligned Bounding Box) collision system
    • PhysicsActor with gravity, friction, and restitution
    • Collision layers and masks for fine control
    • World boundary collision detection
    "},{"location":"getting_started/what_is_pixelroot32/#user-interface","title":"\ud83d\udda5\ufe0f User Interface","text":"
    • Basic elements: Labels, Buttons, Panels
    • Automatic layouts: Vertical, Horizontal, Grid, Anchor
    • Integrated D-pad navigation
    • Scroll and viewport culling for long lists
    "},{"location":"getting_started/what_is_pixelroot32/#optimized-for-esp32","title":"\u26a1 Optimized for ESP32","text":"
    • Efficient memory management
    • Integrated object pooling
    • No dynamic allocations in the game loop
    • Performance optimized for limited hardware
    "},{"location":"getting_started/what_is_pixelroot32/#typical-use-cases","title":"Typical Use Cases","text":"

    PixelRoot32 is ideal for creating:

    • Arcade Games: Space Invaders, Pong, Breakout
    • Platformers: Horizontal scrolling games with simple physics
    • Puzzles: Tetris, Snake, logic games
    • Simple RPGs: Basic role-playing games with tilemaps
    • Shooters: Vertical or horizontal shooting games
    • Rapid Prototypes: Quick development of game ideas
    "},{"location":"getting_started/what_is_pixelroot32/#supported-platforms","title":"Supported Platforms","text":""},{"location":"getting_started/what_is_pixelroot32/#esp32","title":"ESP32","text":"
    • Display: TFT_eSPI (ST7735, ILI9341, ST7789, etc.)
    • Audio: Internal DAC (GPIO 25/26) or external I2S (MAX98357A, PCM5102)
    • Input: Digital buttons, D-pad
    • Hardware: Any ESP32 board (ESP32-WROOM, ESP32-WROVER, etc.)
    "},{"location":"getting_started/what_is_pixelroot32/#desktopnative-pc","title":"Desktop/Native (PC)","text":"
    • Display: SDL2 (Windows, Linux, macOS)
    • Audio: SDL2 Audio
    • Input: Keyboard, mouse
    • Usage: Development, debugging, testing

    Note: Support for u8g2 (OLEDs) is planned for the future.

    "},{"location":"getting_started/what_is_pixelroot32/#project-status","title":"Project Status","text":"

    Current Version: v0.2.0-dev

    PixelRoot32 is under active development. APIs may change and some subsystems are still experimental. Occasional changes or breaking changes are expected, especially on less-tested configurations.

    "},{"location":"getting_started/what_is_pixelroot32/#stable-features","title":"Stable Features","text":"
    • Scene and entity system
    • Basic rendering (1bpp sprites)
    • NES-like audio system
    • Basic physics and collisions
    • Basic UI system
    • ESP32 and Native support
    "},{"location":"getting_started/what_is_pixelroot32/#experimental-features","title":"Experimental Features","text":"
    • 2bpp and 4bpp sprites (require compilation flags)
    • Scene Arena (advanced memory management)
    "},{"location":"getting_started/what_is_pixelroot32/#planned-features","title":"Planned Features","text":"
    • Support for u8g2 (OLEDs)
    • Music compiler
    • Tilemap compiler
    • Save/load system
    • Spatial partitioning for collisions
    "},{"location":"getting_started/what_is_pixelroot32/#quick-comparison","title":"Quick Comparison","text":""},{"location":"getting_started/what_is_pixelroot32/#when-to-use-pixelroot32","title":"When to use PixelRoot32?","text":"

    \u2705 Use PixelRoot32 if: - You want to create retro games on ESP32 - You need a lightweight and efficient engine - You prefer a simple and clear architecture - You want to develop on PC and deploy to ESP32 - You like 8-bit/16-bit style games

    \u274c Don't use PixelRoot32 if: - You need 3D graphics - You require advanced shaders - You need complex physics (advanced physics engines) - You want to create modern AAA games - You need support for multiple mobile platforms

    "},{"location":"getting_started/what_is_pixelroot32/#next-step","title":"Next Step","text":"

    Now that you understand what PixelRoot32 is, discover why you should use it or go directly to your first project.

    See also: - Fundamental Concepts - Installation - API Reference

    "},{"location":"getting_started/why_pixelroot32/","title":"Why PixelRoot32?","text":"

    PixelRoot32 is specifically designed to solve the unique challenges of creating video games on embedded hardware like the ESP32, while maintaining the simplicity and productivity of modern development.

    "},{"location":"getting_started/why_pixelroot32/#main-advantages","title":"Main Advantages","text":""},{"location":"getting_started/why_pixelroot32/#optimized-for-esp32","title":"\ud83c\udfaf Optimized for ESP32","text":"

    Memory Efficient - 1bpp sprite system that minimizes RAM and Flash usage - Integrated object pooling to avoid memory fragmentation - Compact tilemaps that reuse sprites - No dynamic allocations in the game loop

    Performance Optimized - Rendering optimized for ESP32 limitations - Efficient render layer system - Viewport culling to reduce draw calls - Rendering pipeline designed for limited hardware

    Real Hardware - Direct support for common TFT displays (ST7735, ILI9341, ST7789) - Integrated audio (internal DAC or external I2S) - Simple pin and hardware configuration

    "},{"location":"getting_started/why_pixelroot32/#cross-platform-development","title":"\ud83d\udda5\ufe0f Cross-Platform Development","text":"

    Develop on PC, Deploy to ESP32 - Same code works on PC (SDL2) and ESP32 - Fast debugging on desktop - Testing without hardware needed - Rapid development iteration

    Visual Consistency - Native bitmap font system (pixel-perfect) - Same rendering on PC and ESP32 - Consistent color palettes - No surprises when transferring to hardware

    "},{"location":"getting_started/why_pixelroot32/#retro-palette-system","title":"\ud83c\udfa8 Retro Palette System","text":"

    Authentic Style - Predefined palettes: NES, GameBoy, GameBoy Color, PICO-8 - Dual palette mode for visual contrasts - Custom palettes for unique styles - Automatic color resolution (RGB565)

    Easy to Use - Change palette with one line of code - Consistent visualization across all sprites - No need to manually convert assets

    "},{"location":"getting_started/why_pixelroot32/#integrated-audio","title":"\ud83d\udd0a Integrated Audio","text":"

    Complete NES-like System - 4 audio channels (2 Pulse, 1 Triangle, 1 Noise) - Simple sound effects to create - Integrated music system - Backends for different hardware configurations

    No External Dependencies - Software-generated audio - No heavy audio libraries required - Full control over sound - Deterministic and predictable

    "},{"location":"getting_started/why_pixelroot32/#simple-and-clear-architecture","title":"\ud83c\udfd7\ufe0f Simple and Clear Architecture","text":"

    Easy to Understand - Intuitive scene system (inspired by Godot) - Clear hierarchy: Entity \u2192 Actor \u2192 PhysicsActor - Consistent and predictable APIs - Clean and well-organized code

    Quick to Learn - Familiar concepts for game developers - Clear documentation and complete examples - Smooth learning curve - Active community and support

    "},{"location":"getting_started/why_pixelroot32/#complete-features","title":"\ud83c\udfae Complete Features","text":"

    Everything Needed for Games - Rendering (sprites, tilemaps, primitives) - Audio (effects and music) - Physics (gravity, collisions, basic physics) - UI (layouts, buttons, navigation) - Input (buttons, keyboard) - Camera (scroll, parallax)

    No Bloat - Only the essentials, nothing more - No heavy dependencies - Small and maintainable codebase - Easy to understand and modify

    "},{"location":"getting_started/why_pixelroot32/#tools-and-ecosystem","title":"\ud83d\udee0\ufe0f Tools and Ecosystem","text":"

    Available Tools - Sprite Compiler to convert PNG to sprites - Complete game examples - Templates and starter code - Extensive documentation

    Community and Support - Active and developing project - Open source (MIT License) - Feedback and contributions welcome - Examples available

    "},{"location":"getting_started/why_pixelroot32/#comparison-with-alternatives","title":"Comparison with Alternatives","text":""},{"location":"getting_started/why_pixelroot32/#vs-full-engines-unity-godot-etc","title":"vs. Full Engines (Unity, Godot, etc.)","text":"

    PixelRoot32 Advantages: - \u2705 Much lighter (fits in ESP32) - \u2705 No unnecessary overhead - \u2705 Full control over code - \u2705 Specifically optimized for limited hardware

    Disadvantages: - \u274c Fewer advanced features - \u274c No visual editor - \u274c Fewer resources and community

    "},{"location":"getting_started/why_pixelroot32/#vs-writing-everything-from-scratch","title":"vs. Writing Everything from Scratch","text":"

    PixelRoot32 Advantages: - \u2705 Rendering system already implemented - \u2705 Integrated and working audio - \u2705 Physics and collisions ready - \u2705 Complete UI system - \u2705 Saves months of development

    Disadvantages: - \u274c Less control over internal implementation - \u274c You must learn the engine API

    "},{"location":"getting_started/why_pixelroot32/#vs-other-esp32-engines","title":"vs. Other ESP32 Engines","text":"

    PixelRoot32 Advantages: - \u2705 More modern and clear architecture - \u2705 Better documentation - \u2705 Unique palette system - \u2705 Integrated NES-like audio - \u2705 Real cross-platform development

    "},{"location":"getting_started/why_pixelroot32/#ideal-use-cases","title":"Ideal Use Cases","text":"

    PixelRoot32 is perfect for:

    1. Educational Projects
    2. Learn game development
    3. Understand engine architecture
    4. Student projects

    5. Rapid Prototypes

    6. Quickly validate game ideas
    7. Create demos and proof-of-concepts
    8. Test mechanics

    9. Retro Games

    10. 8-bit/16-bit style games
    11. Arcade games
    12. Games with retro aesthetics

    13. Hardware Projects

    14. Games on small displays
    15. DIY portable consoles
    16. Maker/retro projects

    17. C++ Learning

    18. Clean and well-structured code
    19. Good programming practices
    20. Real and functional examples
    "},{"location":"getting_started/why_pixelroot32/#limitations-to-consider","title":"Limitations to Consider","text":"

    To be honest, PixelRoot32 has limitations:

    • Limited Hardware: Designed for ESP32, not powerful PCs
    • Simple Graphics: No 3D, no advanced shaders
    • Basic Physics: Not a complete physics engine
    • Restricted Memory: MAX_ENTITIES = 32 per scene
    • In Development: Some features are experimental

    If you need advanced features or powerful hardware, consider other engines. But for retro games on ESP32, PixelRoot32 is an excellent choice.

    "},{"location":"getting_started/why_pixelroot32/#conclusion","title":"Conclusion","text":"

    PixelRoot32 combines:

    • \u2705 Simplicity of use
    • \u2705 Efficiency for limited hardware
    • \u2705 Completeness of essential features
    • \u2705 Clarity of architecture
    • \u2705 Productivity in development

    If you want to create retro games on ESP32 without the complexity of large engines, PixelRoot32 is the right choice.

    "},{"location":"getting_started/why_pixelroot32/#next-step","title":"Next Step","text":"

    Now that you understand why PixelRoot32 is a good option, learn the fundamental concepts or start directly with your first project.

    See also: - What is PixelRoot32? - Fundamental Concepts - Your First Project

    "},{"location":"getting_started/your_first_project/","title":"Your First Project","text":"

    This guide will walk you through creating and running your first PixelRoot32 project step by step. By the end, you'll have a working project that displays a simple scene on both ESP32 and PC.

    "},{"location":"getting_started/your_first_project/#prerequisites","title":"Prerequisites","text":""},{"location":"getting_started/your_first_project/#required-software","title":"Required Software","text":"
    • PlatformIO: Install the PlatformIO IDE extension in VS Code
    • Open VS Code
    • Go to Extensions (Ctrl+Shift+X)
    • Search for \"PlatformIO IDE\"
    • Install and restart VS Code

    • Python 3.8+: Required for PlatformIO (usually installed automatically)

    "},{"location":"getting_started/your_first_project/#for-esp32-development","title":"For ESP32 Development","text":"
    • ESP32 Board: Any ESP32 development board (ESP32-WROOM, ESP32-WROVER, etc.)
    • USB Cable: To connect and program your ESP32
    • TFT Display: Compatible display (ST7735, ST7789, ILI9341, etc.)
    • Buttons: 5-6 digital buttons for input (optional for first project)
    • Audio Hardware (optional): Speaker + amplifier (PAM8302A) or I2S DAC (MAX98357A)
    "},{"location":"getting_started/your_first_project/#for-native-pc-development","title":"For Native (PC) Development","text":"
    • SDL2: Development libraries
    • Windows (MSYS2): pacman -S mingw-w64-x86_64-SDL2
    • Linux: sudo apt-get install libsdl2-dev
    • macOS: brew install sdl2
    "},{"location":"getting_started/your_first_project/#step-1-create-a-new-platformio-project","title":"Step 1: Create a New PlatformIO Project","text":"
    1. Open VS Code with PlatformIO installed

    2. Create New Project:

    3. Click on the PlatformIO icon in the sidebar
    4. Click \"New Project\"
    5. Name: my-first-pixelroot32-game
    6. Board: Select \"ESP32 Dev Module\" (or your specific board)
    7. Framework: Arduino
    8. Location: Choose your workspace folder
    9. Click \"Finish\"

    10. Project Structure: Your project should now have this structure:

      my-first-pixelroot32-game/\n\u251c\u2500\u2500 .pio/\n\u251c\u2500\u2500 include/\n\u251c\u2500\u2500 lib/\n\u251c\u2500\u2500 src/\n\u2502   \u2514\u2500\u2500 main.cpp\n\u251c\u2500\u2500 test/\n\u2514\u2500\u2500 platformio.ini\n

    "},{"location":"getting_started/your_first_project/#step-2-install-pixelroot32-engine","title":"Step 2: Install PixelRoot32 Engine","text":""},{"location":"getting_started/your_first_project/#option-a-via-platformio-library-manager-recommended","title":"Option A: Via PlatformIO Library Manager (Recommended)","text":"
    1. Open platformio.ini

    2. Add the library dependency:

    [env:esp32dev]\nplatform = espressif32\nboard = esp32dev\nframework = arduino\nlib_deps = \n    gperez88/PixelRoot32-Game-Engine@0.2.0-dev\n

    \u26a0\ufe0f IMPORTANT: Use the exact version 0.2.0-dev. Do NOT use ^ or fuzzy versioning.

    1. Save the file. PlatformIO will automatically download the library.
    "},{"location":"getting_started/your_first_project/#option-b-git-submodule","title":"Option B: Git Submodule","text":"
    1. Open terminal in your project root

    2. Add as submodule:

      git submodule add https://github.com/Gperez88/PixelRoot32-Game-Engine.git lib/PixelRoot32-Game-Engine\n

    3. Update platformio.ini:

      lib_extra_dirs = lib\n

    "},{"location":"getting_started/your_first_project/#step-3-configure-hardware-esp32","title":"Step 3: Configure Hardware (ESP32)","text":""},{"location":"getting_started/your_first_project/#configure-tft_espi-display","title":"Configure TFT_eSPI Display","text":"

    Edit platformio.ini and add build flags for your display. Here are two common configurations:

    For ST7789 (240x240):

    [env:esp32dev]\nplatform = espressif32\nboard = esp32dev\nframework = arduino\nlib_deps = \n    gperez88/PixelRoot32-Game-Engine@0.2.0-dev\n    bodmer/TFT_eSPI@^2.5.43\n\nbuild_flags = \n    -D ST7789_DRIVER\n    -D TFT_WIDTH=240\n    -D TFT_HEIGHT=240\n    -D TFT_MOSI=23\n    -D TFT_SCLK=18\n    -D TFT_DC=2\n    -D TFT_RST=4\n    -D TFT_CS=-1\n    -D LOAD_GLCD\n    -D LOAD_FONT2\n    -D LOAD_FONT4\n    -D LOAD_FONT6\n    -D LOAD_FONT7\n    -D LOAD_FONT8\n    -D LOAD_GFXFF\n    -D SMOOTH_FONT\n    -D SPI_FREQUENCY=40000000\n    -D SPI_READ_FREQUENCY=20000000\n

    For ST7735 (128x128):

    build_flags = \n    -D ST7735_DRIVER\n    -D ST7735_GREENTAB3\n    -D TFT_WIDTH=128\n    -D TFT_HEIGHT=128\n    -D TFT_MOSI=23\n    -D TFT_SCLK=18\n    -D TFT_DC=2\n    -D TFT_RST=4\n    -D TFT_CS=-1\n    -D LOAD_GLCD\n    -D LOAD_FONT2\n    -D LOAD_FONT4\n    -D LOAD_FONT6\n    -D LOAD_FONT7\n    -D LOAD_FONT8\n    -D LOAD_GFXFF\n    -D SMOOTH_FONT\n    -D SPI_FREQUENCY=27000000\n    -D SPI_READ_FREQUENCY=20000000\n

    Note: Adjust the pin numbers (TFT_MOSI, TFT_SCLK, TFT_DC, TFT_RST) to match your hardware wiring.

    "},{"location":"getting_started/your_first_project/#configure-input-optional-for-first-project","title":"Configure Input (Optional for First Project)","text":"

    If you have buttons connected, note the GPIO pins. For now, we'll create a project that works without input.

    "},{"location":"getting_started/your_first_project/#configure-audio-optional-for-first-project","title":"Configure Audio (Optional for First Project)","text":"

    Audio is optional for the first project. We'll add it later.

    "},{"location":"getting_started/your_first_project/#step-4-create-your-first-scene","title":"Step 4: Create Your First Scene","text":"

    Create a new file src/MyFirstScene.h:

    #pragma once\n#include <core/Scene.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\nclass MyFirstScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Called when the scene is initialized\n        // Set up your scene here\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Called every frame\n        // Update game logic here\n        Scene::update(deltaTime); // Don't forget to call parent update!\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Called every frame to draw\n        // Draw your scene here\n\n        // Example: Draw a simple rectangle\n        renderer.drawFilledRectangle(50, 50, 100, 100, pixelroot32::graphics::Color::Blue);\n\n        // Example: Draw text\n        renderer.drawText(\"Hello PixelRoot32!\", 20, 20, pixelroot32::graphics::Color::White, 2);\n\n        // Don't forget to call parent draw to draw all entities!\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"getting_started/your_first_project/#step-5-create-main-file-esp32","title":"Step 5: Create Main File (ESP32)","text":"

    Replace the contents of src/main.cpp with:

    #include <Arduino.h>\n#include <core/Engine.h>\n#include <drivers/esp32/TFT_eSPI_Drawer.h>\n#include <drivers/esp32/ESP32_DAC_AudioBackend.h>\n#include \"MyFirstScene.h\"\n\nnamespace pr32 = pixelroot32;\n\n// Audio configuration (optional - can be omitted for first project)\nconst int DAC_PIN = 25; // GPIO 25 or 26\npr32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(DAC_PIN, 11025);\n\n// Display configuration\n// ST7789, rotation 0, 240x240 resolution\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::ST7789, \n    0,      // rotation\n    240,    // width\n    240     // height\n);\n\n// Input configuration (6 buttons: UP, DOWN, LEFT, RIGHT, A, B)\n// For now, we'll use dummy pins - you can change these later\npr32::input::InputConfig inputConfig(\n    6,      // button count\n    32,     // UP pin\n    27,     // DOWN pin\n    33,     // LEFT pin\n    14,     // RIGHT pin\n    13,     // A button pin\n    12      // B button pin\n);\n\n// Audio configuration\npr32::audio::AudioConfig audioConfig(&audioBackend, audioBackend.getSampleRate());\n\n// Create the engine\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\n// Create your scene\nMyFirstScene myScene;\n\nvoid setup() {\n    Serial.begin(115200);\n\n    // Initialize the engine\n    engine.init();\n\n    // Initialize and set the scene\n    myScene.init();\n    engine.setScene(&myScene);\n\n    Serial.println(\"PixelRoot32 initialized!\");\n}\n\nvoid loop() {\n    // Run the game loop\n    engine.run();\n}\n
    "},{"location":"getting_started/your_first_project/#step-6-create-native-version-optional","title":"Step 6: Create Native Version (Optional)","text":"

    If you want to test on PC first, create src/main_native.cpp:

    #define SDL_MAIN_HANDLED\n#include <SDL2/SDL.h>\n#include <core/Engine.h>\n#include <drivers/native/SDL2_Drawer.h>\n#include <drivers/native/SDL2_AudioBackend.h>\n#include \"MyFirstScene.h\"\n\nnamespace pr32 = pixelroot32;\n\n// Audio configuration\npr32::drivers::native::SDL2_AudioBackend audioBackend(22050, 1024);\n\n// Display configuration (NONE defaults to SDL2 on Native)\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::NONE,\n    0,      // rotation\n    240,    // width\n    240     // height\n);\n\n// Input configuration (SDL scancodes)\npr32::input::InputConfig inputConfig(\n    6,                      // button count\n    SDL_SCANCODE_UP,        // UP\n    SDL_SCANCODE_DOWN,      // DOWN\n    SDL_SCANCODE_LEFT,      // LEFT\n    SDL_SCANCODE_RIGHT,     // RIGHT\n    SDL_SCANCODE_SPACE,     // A button\n    SDL_SCANCODE_RETURN     // B button\n);\n\n// Audio configuration\npr32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n\n// Create the engine\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\n// Create your scene\nMyFirstScene myScene;\n\nint main(int argc, char* argv[]) {\n    (void)argc;\n    (void)argv;\n\n    // Initialize the engine\n    engine.init();\n\n    // Initialize and set the scene\n    myScene.init();\n    engine.setScene(&myScene);\n\n    // Run the game loop\n    engine.run();\n\n    return 0;\n}\n
    "},{"location":"getting_started/your_first_project/#configure-native-build","title":"Configure Native Build","text":"

    Add to platformio.ini:

    [env:native]\nplatform = native\nbuild_src_filter = \n    +<*>\n    -<main.cpp>\nlib_extra_dirs = lib\nbuild_flags = \n    -D PLATFORM_NATIVE\n    -Isrc\n    -Ilib/PixelRoot32-Game-Engine/include\n    -IC:/msys64/mingw64/include/SDL2    # Windows MSYS2 path - adjust for your system\n    -LC:/msys64/mingw64/lib             # Windows MSYS2 path - adjust for your system\n    -O2\n    -Wall\n    -Wextra\n    -std=c++17\n    -lSDL2\n    -mconsole\n

    Note: Adjust the SDL2 include and library paths for your system.

    "},{"location":"getting_started/your_first_project/#step-7-build-and-run","title":"Step 7: Build and Run","text":""},{"location":"getting_started/your_first_project/#for-esp32","title":"For ESP32","text":"
    1. Connect your ESP32 via USB
    2. Select the environment: Click on the PlatformIO icon \u2192 Select env:esp32dev
    3. Build: Click the checkmark icon (\u2713) or press Ctrl+Alt+B
    4. Upload: Click the arrow icon (\u2192) or press Ctrl+Alt+U
    5. Monitor: Click the plug icon to open serial monitor

    You should see \"PixelRoot32 initialized!\" in the serial monitor and your display should show a blue rectangle and text.

    "},{"location":"getting_started/your_first_project/#for-native-pc","title":"For Native (PC)","text":"
    1. Select the environment: Click on the PlatformIO icon \u2192 Select env:native
    2. Build and Run: Click the play icon (\u25b6) or press Ctrl+Alt+R

    A window should open showing your scene with a blue rectangle and \"Hello PixelRoot32!\" text.

    "},{"location":"getting_started/your_first_project/#step-8-verify-it-works","title":"Step 8: Verify It Works","text":"

    If everything is set up correctly, you should see:

    • ESP32: Display shows a blue rectangle at (50, 50) and white text \"Hello PixelRoot32!\" at (20, 20)
    • Native: Window shows the same content

    If you see this, congratulations! Your first PixelRoot32 project is working.

    "},{"location":"getting_started/your_first_project/#troubleshooting","title":"Troubleshooting","text":""},{"location":"getting_started/your_first_project/#esp32-issues","title":"ESP32 Issues","text":"

    Display is blank: - Check wiring connections - Verify pin numbers in platformio.ini match your hardware - Check SPI frequency (try lowering it) - Verify display type (ST7789 vs ST7735)

    Compilation errors: - Ensure library version is exactly 0.2.0-dev - Check that TFT_eSPI is installed - Verify all include paths are correct

    Upload fails: - Check USB cable connection - Try different USB port - Press BOOT button on ESP32 during upload - Check COM port in PlatformIO

    "},{"location":"getting_started/your_first_project/#native-issues","title":"Native Issues","text":"

    SDL2 not found: - Verify SDL2 is installed - Check include/library paths in platformio.ini - On Windows, ensure MSYS2 paths are correct

    Window doesn't open: - Check console for error messages - Verify SDL2 is properly linked - Try running from terminal to see errors

    "},{"location":"getting_started/your_first_project/#next-steps","title":"Next Steps","text":"

    Now that you have a working project, you can:

    1. Learn about Scenes and Entities: See how to create game objects
    2. Add Input: Make your scene respond to buttons
    3. Add Sprites: Draw custom graphics
    4. Add Audio: Play sounds and music

    Continue with the Development Guide to learn more.

    See also: - Fundamental Concepts - Installation - Manual - Scenes and Entities - API Reference

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/","title":"Cameras and Scrolling","text":"

    Camera2D allows you to create worlds larger than the screen by scrolling the view. This guide covers camera setup, following targets, boundaries, and parallax effects.

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#camera2d-basics","title":"Camera2D Basics","text":"

    A Camera2D defines what portion of your game world is visible on screen.

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#creating-a-camera","title":"Creating a Camera","text":"
    #include <graphics/Camera2D.h>\n\n// Create camera with viewport size\npixelroot32::graphics::Camera2D camera(240, 240); // Screen width, height\n\n// Set camera position\ncamera.setPosition(0, 0);\n\n// Apply camera to renderer (in draw method)\ncamera.apply(renderer);\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#how-it-works","title":"How It Works","text":"

    The camera translates world coordinates to screen coordinates: - Objects at world position (100, 50) with camera at (0, 0) appear at screen (100, 50) - Objects at world position (100, 50) with camera at (50, 0) appear at screen (50, 50) - The camera effectively \"moves\" the world relative to the screen

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#following-a-target","title":"Following a Target","text":"

    The most common use is following a player or other target.

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#basic-follow","title":"Basic Follow","text":"
    class GameScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    PlayerActor* player;\n\npublic:\n    void init() override {\n        int screenWidth = engine.getRenderer().getWidth();\n        int screenHeight = engine.getRenderer().getHeight();\n\n        // Create camera\n        camera = pixelroot32::graphics::Camera2D(screenWidth, screenHeight);\n\n        // Create player\n        player = new PlayerActor(500, 300); // World position\n        addEntity(player);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Make camera follow player\n        camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Apply camera before drawing\n        camera.apply(renderer);\n\n        // Now all drawing uses camera coordinates\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#dead-zone-smooth-following","title":"Dead Zone (Smooth Following)","text":"

    For smoother following, you can implement a dead zone where the camera doesn't move until the target leaves the zone:

    void update(unsigned long deltaTime) override {\n    Scene::update(deltaTime);\n\n    // Get screen center\n    int screenCenterX = engine.getRenderer().getWidth() / 2;\n    int screenCenterY = engine.getRenderer().getHeight() / 2;\n\n    // Calculate player position relative to screen center\n    float playerScreenX = player->x - camera.getX();\n    float playerScreenY = player->y - camera.getY();\n\n    // Dead zone size\n    const int DEAD_ZONE_X = 40;\n    const int DEAD_ZONE_Y = 40;\n\n    // Move camera if player leaves dead zone\n    if (playerScreenX < screenCenterX - DEAD_ZONE_X) {\n        camera.setPosition(player->x - (screenCenterX - DEAD_ZONE_X), camera.getY());\n    } else if (playerScreenX > screenCenterX + DEAD_ZONE_X) {\n        camera.setPosition(player->x - (screenCenterX + DEAD_ZONE_X), camera.getY());\n    }\n\n    if (playerScreenY < screenCenterY - DEAD_ZONE_Y) {\n        camera.setPosition(camera.getX(), player->y - (screenCenterY - DEAD_ZONE_Y));\n    } else if (playerScreenY > screenCenterY + DEAD_ZONE_Y) {\n        camera.setPosition(camera.getX(), player->y - (screenCenterY + DEAD_ZONE_Y));\n    }\n}\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#camera-boundaries","title":"Camera Boundaries","text":"

    Limit camera movement to keep it within your level bounds.

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#setting-boundaries","title":"Setting Boundaries","text":"
    void init() override {\n    // Create camera\n    camera = pixelroot32::graphics::Camera2D(240, 240);\n\n    // Set horizontal boundaries (level is 2000 pixels wide)\n    camera.setBounds(0, 2000 - 240); // minX, maxX\n\n    // Set vertical boundaries (level is 1000 pixels tall)\n    camera.setVerticalBounds(0, 1000 - 240); // minY, maxY\n}\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#example-side-scroller-with-boundaries","title":"Example: Side-Scroller with Boundaries","text":"
    class SideScrollerScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    PlayerActor* player;\n    static const int LEVEL_WIDTH = 2000;\n    static const int LEVEL_HEIGHT = 240;\n\npublic:\n    void init() override {\n        int screenWidth = engine.getRenderer().getWidth();\n        int screenHeight = engine.getRenderer().getHeight();\n\n        camera = pixelroot32::graphics::Camera2D(screenWidth, screenHeight);\n\n        // Set boundaries (camera can't go outside level)\n        camera.setBounds(0, LEVEL_WIDTH - screenWidth);\n        camera.setVerticalBounds(0, LEVEL_HEIGHT - screenHeight);\n\n        // Create player at start\n        player = new PlayerActor(100, 100);\n        addEntity(player);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Follow player horizontally\n        camera.followTarget(player->x, camera.getY());\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        camera.apply(renderer);\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#parallax-scrolling","title":"Parallax Scrolling","text":"

    Parallax creates depth by moving background layers at different speeds.

    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#basic-parallax","title":"Basic Parallax","text":"
    class ParallaxBackground : public pixelroot32::core::Entity {\nprivate:\n    float parallaxSpeed; // 0.0 to 1.0 (1.0 = normal, 0.5 = half speed)\n    float baseX;\n\npublic:\n    ParallaxBackground(float speed)\n        : Entity(0, 0, 240, 240, pixelroot32::core::EntityType::GENERIC),\n          parallaxSpeed(speed), baseX(0) {\n        setRenderLayer(0);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Get camera position\n        auto& camera = getCamera(); // You'll need to pass camera reference\n\n        // Calculate parallax offset\n        baseX = camera.getX() * parallaxSpeed;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw background with parallax offset\n        renderer.drawTileMap(backgroundTileMap, \n            static_cast<int>(baseX), 0, \n            pixelroot32::graphics::Color::White);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#multiple-parallax-layers","title":"Multiple Parallax Layers","text":"
    class ParallaxScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n\n    // Parallax layers (farther = slower)\n    ParallaxLayer* farBackground;    // Speed: 0.2\n    ParallaxLayer* midBackground;      // Speed: 0.5\n    ParallaxLayer* nearBackground;     // Speed: 0.8\n    PlayerActor* player;               // Speed: 1.0 (normal)\n\npublic:\n    void init() override {\n        camera = pixelroot32::graphics::Camera2D(240, 240);\n\n        // Create parallax layers\n        farBackground = new ParallaxLayer(0.2f);  // Moves slowest\n        midBackground = new ParallaxLayer(0.5f);\n        nearBackground = new ParallaxLayer(0.8f);\n\n        addEntity(farBackground);\n        addEntity(midBackground);\n        addEntity(nearBackground);\n\n        player = new PlayerActor(100, 100);\n        addEntity(player);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Update parallax layers with camera position\n        farBackground->updateParallax(camera.getX());\n        midBackground->updateParallax(camera.getX());\n        nearBackground->updateParallax(camera.getX());\n\n        // Follow player\n        camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        camera.apply(renderer);\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#using-setdisplayoffset-for-parallax","title":"Using setDisplayOffset for Parallax","text":"

    For simpler parallax, you can use setDisplayOffset():

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Apply camera\n    camera.apply(renderer);\n\n    // Draw far background with offset (moves slower)\n    renderer.setDisplayOffset(\n        static_cast<int>(camera.getX() * 0.3f), \n        0\n    );\n    renderer.drawTileMap(farBackground, 0, 0, Color::White);\n\n    // Draw mid background\n    renderer.setDisplayOffset(\n        static_cast<int>(camera.getX() * 0.6f), \n        0\n    );\n    renderer.drawTileMap(midBackground, 0, 0, Color::White);\n\n    // Reset offset for normal drawing\n    renderer.setDisplayOffset(0, 0);\n\n    // Draw game objects (normal speed)\n    Scene::draw(renderer);\n}\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#complete-example-platformer-with-camera","title":"Complete Example: Platformer with Camera","text":"
    class PlatformerScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    PlayerActor* player;\n    static const int LEVEL_WIDTH = 3000;\n    static const int LEVEL_HEIGHT = 800;\n\npublic:\n    void init() override {\n        int screenWidth = engine.getRenderer().getWidth();\n        int screenHeight = engine.getRenderer().getHeight();\n\n        // Create camera\n        camera = pixelroot32::graphics::Camera2D(screenWidth, screenHeight);\n\n        // Set boundaries\n        camera.setBounds(0, LEVEL_WIDTH - screenWidth);\n        camera.setVerticalBounds(0, LEVEL_HEIGHT - screenHeight);\n\n        // Create player\n        player = new PlayerActor(100, 400);\n        addEntity(player);\n\n        // Create platforms, enemies, etc.\n        // ...\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Follow player with dead zone\n        int screenCenterX = engine.getRenderer().getWidth() / 2;\n        int screenCenterY = engine.getRenderer().getHeight() / 2;\n\n        float playerScreenX = player->x - camera.getX();\n        float playerScreenY = player->y - camera.getY();\n\n        const int DEAD_ZONE = 60;\n\n        // Horizontal follow\n        if (playerScreenX < screenCenterX - DEAD_ZONE) {\n            camera.setPosition(player->x - (screenCenterX - DEAD_ZONE), camera.getY());\n        } else if (playerScreenX > screenCenterX + DEAD_ZONE) {\n            camera.setPosition(player->x - (screenCenterX + DEAD_ZONE), camera.getY());\n        }\n\n        // Vertical follow (only when falling or jumping high)\n        if (playerScreenY < screenCenterY - DEAD_ZONE || \n            playerScreenY > screenCenterY + DEAD_ZONE) {\n            camera.setPosition(camera.getX(), player->y - screenCenterY);\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Apply camera\n        camera.apply(renderer);\n\n        // Draw background (parallax)\n        renderer.setDisplayOffset(\n            static_cast<int>(camera.getX() * 0.3f), \n            0\n        );\n        renderer.drawTileMap(backgroundTileMap, 0, 0, Color::DarkGray);\n        renderer.setDisplayOffset(0, 0);\n\n        // Draw game objects\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#best-practices","title":"Best Practices","text":""},{"location":"manual/advanced_graphics/cameras_and_scrolling/#camera-movement","title":"Camera Movement","text":"
    • Use dead zones: Prevents jittery camera movement
    • Smooth transitions: Consider lerping camera position for smoother movement
    • Set boundaries: Always limit camera to level bounds
    • Test on hardware: Camera performance may differ on ESP32
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#parallax","title":"Parallax","text":"
    • Layer speeds: Farther layers move slower (0.2-0.5), closer move faster (0.7-0.9)
    • Limit layers: Too many parallax layers can impact performance
    • Use tilemaps: Parallax works best with tilemaps
    • Test visually: Ensure parallax effect is noticeable but not distracting
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#performance","title":"Performance","text":"
    • Apply once: Call camera.apply() once per frame, at start of draw()
    • Cull off-screen: Don't draw entities outside camera view
    • Limit parallax layers: 2-3 layers is usually enough
    • Optimize tilemaps: Use efficient tilemap rendering
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/advanced_graphics/cameras_and_scrolling/#camera-helper-class","title":"Camera Helper Class","text":"
    class CameraController {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    float targetX, targetY;\n    float smoothSpeed = 0.1f;\n\npublic:\n    void followTarget(float x, float y) {\n        targetX = x;\n        targetY = y;\n    }\n\n    void update(unsigned long deltaTime) {\n        // Smooth camera movement\n        float currentX = camera.getX();\n        float currentY = camera.getY();\n\n        float newX = currentX + (targetX - currentX) * smoothSpeed;\n        float newY = currentY + (targetY - currentY) * smoothSpeed;\n\n        camera.setPosition(newX, newY);\n    }\n\n    void apply(pixelroot32::graphics::Renderer& renderer) {\n        camera.apply(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#viewport-culling","title":"Viewport Culling","text":"

    Only draw entities within camera view:

    bool isVisible(float x, float y, int width, int height) {\n    float cameraX = camera.getX();\n    float cameraY = camera.getY();\n    int screenWidth = engine.getRenderer().getWidth();\n    int screenHeight = engine.getRenderer().getHeight();\n\n    return !(x + width < cameraX || \n             x > cameraX + screenWidth ||\n             y + height < cameraY || \n             y > cameraY + screenHeight);\n}\n
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/advanced_graphics/cameras_and_scrolling/#camera-not-moving","title":"Camera Not Moving","text":"
    • Verify camera.apply() is called in draw()
    • Check followTarget() or setPosition() is called in update()
    • Ensure camera is created with correct viewport size
    • Check boundaries aren't preventing movement
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#objects-not-visible","title":"Objects Not Visible","text":"
    • Verify objects are within camera view
    • Check world coordinates vs screen coordinates
    • Ensure camera is applied before drawing
    • Verify render layers are correct
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#parallax-not-working","title":"Parallax Not Working","text":"
    • Check setDisplayOffset() is used correctly
    • Verify parallax speed values (0.0 to 1.0)
    • Ensure offset is reset after parallax layers
    • Test with different speed values
    "},{"location":"manual/advanced_graphics/cameras_and_scrolling/#next-steps","title":"Next Steps","text":"

    Now that you understand cameras and scrolling, learn about: - Tilemaps - Build levels with tiles - Particles and Effects - Add visual effects - Performance Optimization - Optimize your game

    See also: - API Reference - Camera2D - Manual - Basic Rendering - Manual - Tilemaps

    "},{"location":"manual/advanced_graphics/color_palettes/","title":"Color Palettes","text":"

    PixelRoot32 uses a palette-based color system that allows you to easily change the visual style of your game. This guide covers built-in palettes, dual palette mode, and custom palettes.

    "},{"location":"manual/advanced_graphics/color_palettes/#built-in-palettes","title":"Built-in Palettes","text":"

    PixelRoot32 includes several predefined palettes inspired by classic gaming systems:

    "},{"location":"manual/advanced_graphics/color_palettes/#available-palettes","title":"Available Palettes","text":"
    #include <graphics/PaletteDefs.h>\n\nnamespace pixelroot32::graphics {\n\nenum class PaletteType {\n    PR32,    // PixelRoot32 default palette\n    NES,     // Nintendo Entertainment System\n    GB,      // GameBoy (4 shades of green)\n    GBC,     // GameBoy Color\n    PICO8    // PICO-8 palette\n};\n
    "},{"location":"manual/advanced_graphics/color_palettes/#using-built-in-palettes","title":"Using Built-in Palettes","text":"
    #include <graphics/PaletteDefs.h>\n\n// Set palette globally (legacy mode)\npixelroot32::graphics::setPalette(pixelroot32::graphics::PaletteType::NES);\n\n// All sprites will now use NES colors\nrenderer.drawSprite(MY_SPRITE, 100, 100, pixelroot32::graphics::Color::White);\n
    "},{"location":"manual/advanced_graphics/color_palettes/#palette-characteristics","title":"Palette Characteristics","text":"

    PR32 (Default) - Modern, balanced colors - Good contrast - Suitable for most games

    NES - Classic 8-bit console colors - Limited color range - Nostalgic feel

    GB (GameBoy) - 4 shades of green - Monochrome aesthetic - Classic handheld look

    GBC (GameBoy Color) - Expanded color range - More vibrant than GB - Classic portable console

    PICO8 - PICO-8 fantasy console palette - 16 carefully chosen colors - Popular for retro games

    "},{"location":"manual/advanced_graphics/color_palettes/#legacy-mode-single-global-palette","title":"Legacy Mode (Single Global Palette)","text":"

    In legacy mode, one palette is used for all sprites:

    void MyScene::init() override {\n    // Set global palette\n    pixelroot32::graphics::setPalette(pixelroot32::graphics::PaletteType::NES);\n\n    // All sprites use NES colors\n    // This is the simplest mode\n}\n

    When to use: - Simple games - Consistent visual style - Maximum compatibility

    "},{"location":"manual/advanced_graphics/color_palettes/#dual-palette-mode","title":"Dual Palette Mode","text":"

    Dual palette mode allows different palettes for background elements and sprites, creating visual contrast.

    "},{"location":"manual/advanced_graphics/color_palettes/#enabling-dual-palette-mode","title":"Enabling Dual Palette Mode","text":"
    #include <graphics/PaletteDefs.h>\n\nvoid MyScene::init() override {\n    // Enable dual palette mode\n    pixelroot32::graphics::enableDualPaletteMode();\n\n    // Set background palette\n    pixelroot32::graphics::setBackgroundPalette(\n        pixelroot32::graphics::PaletteType::GB\n    );\n\n    // Set sprite palette\n    pixelroot32::graphics::setSpritePalette(\n        pixelroot32::graphics::PaletteType::NES\n    );\n}\n
    "},{"location":"manual/advanced_graphics/color_palettes/#how-it-works","title":"How It Works","text":"
    • Background palette: Used for tilemaps, primitives, and background sprites
    • Sprite palette: Used for game objects, characters, and foreground sprites
    • Automatic context: The renderer automatically selects the correct palette based on what you're drawing
    "},{"location":"manual/advanced_graphics/color_palettes/#example-contrasting-styles","title":"Example: Contrasting Styles","text":"
    void MyScene::init() override {\n    pixelroot32::graphics::enableDualPaletteMode();\n\n    // Dark, muted background (GameBoy green)\n    pixelroot32::graphics::setBackgroundPalette(\n        pixelroot32::graphics::PaletteType::GB\n    );\n\n    // Bright, colorful sprites (NES)\n    pixelroot32::graphics::setSpritePalette(\n        pixelroot32::graphics::PaletteType::NES\n    );\n\n    // Background uses GB palette\n    renderer.drawTileMap(backgroundTileMap, 0, 0, \n        pixelroot32::graphics::Color::White);\n\n    // Sprites use NES palette\n    renderer.drawSprite(playerSprite, 100, 100, \n        pixelroot32::graphics::Color::White);\n}\n
    "},{"location":"manual/advanced_graphics/color_palettes/#when-to-use-dual-palette-mode","title":"When to Use Dual Palette Mode","text":"
    • Visual contrast: Make sprites stand out from background
    • Artistic style: Different palettes for different layers
    • Retro aesthetics: Classic console color separation
    • Performance: No performance impact, just visual variety
    "},{"location":"manual/advanced_graphics/color_palettes/#custom-palettes","title":"Custom Palettes","text":"

    Create your own color palettes for unique visual styles.

    "},{"location":"manual/advanced_graphics/color_palettes/#creating-a-custom-palette","title":"Creating a Custom Palette","text":"
    #include <graphics/PaletteDefs.h>\n#include <graphics/Color.h>\n\n// Define custom colors (RGB565 format)\nstatic const pixelroot32::graphics::Color CUSTOM_PALETTE[] = {\n    pixelroot32::graphics::Color::Black,      // 0: Transparent/background\n    pixelroot32::graphics::Color::DarkBlue,   // 1\n    pixelroot32::graphics::Color::Blue,       // 2\n    pixelroot32::graphics::Color::LightBlue, // 3\n    pixelroot32::graphics::Color::Cyan,      // 4\n    pixelroot32::graphics::Color::White,      // 5\n    // ... more colors\n};\n\n// Set custom palette\npixelroot32::graphics::setCustomPalette(\n    CUSTOM_PALETTE,\n    sizeof(CUSTOM_PALETTE) / sizeof(pixelroot32::graphics::Color)\n);\n
    "},{"location":"manual/advanced_graphics/color_palettes/#rgb565-color-format","title":"RGB565 Color Format","text":"

    Colors in PixelRoot32 use RGB565 format (16-bit):

    // RGB565: 5 bits red, 6 bits green, 5 bits blue\n// Format: RRRRR GGGGGG BBBBB\n\n// Create custom RGB565 color\nuint16_t myColor = (31 << 11) | (63 << 5) | 31; // White\nuint16_t myColor = (0 << 11) | (0 << 5) | 0;    // Black\nuint16_t myColor = (31 << 11) | (0 << 5) | 0;   // Red\n\n// Or use Color constants\npixelroot32::graphics::Color::Red\npixelroot32::graphics::Color::Green\npixelroot32::graphics::Color::Blue\n
    "},{"location":"manual/advanced_graphics/color_palettes/#helper-function-for-custom-colors","title":"Helper Function for Custom Colors","text":"
    // Create RGB565 color from RGB values (0-255)\nuint16_t rgb565(uint8_t r, uint8_t g, uint8_t b) {\n    return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);\n}\n\n// Usage\nstatic const pixelroot32::graphics::Color MY_PALETTE[] = {\n    rgb565(0, 0, 0),        // Black\n    rgb565(255, 0, 0),      // Red\n    rgb565(0, 255, 0),      // Green\n    rgb565(0, 0, 255),      // Blue\n    rgb565(255, 255, 255),  // White\n};\n
    "},{"location":"manual/advanced_graphics/color_palettes/#complete-custom-palette-example","title":"Complete Custom Palette Example","text":"
    class CustomPaletteScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Define custom palette (ocean theme)\n        static const pixelroot32::graphics::Color OCEAN_PALETTE[] = {\n            pixelroot32::graphics::Color::Black,      // 0: Deep ocean\n            pixelroot32::graphics::Color::Navy,        // 1: Dark blue\n            pixelroot32::graphics::Color::Blue,       // 2: Medium blue\n            pixelroot32::graphics::Color::Cyan,       // 3: Light blue\n            pixelroot32::graphics::Color::LightBlue, // 4: Surface\n            pixelroot32::graphics::Color::White,      // 5: Foam\n        };\n\n        // Set custom palette\n        pixelroot32::graphics::setCustomPalette(\n            OCEAN_PALETTE,\n            sizeof(OCEAN_PALETTE) / sizeof(pixelroot32::graphics::Color)\n        );\n\n        // Now all sprites use the ocean palette\n    }\n};\n
    "},{"location":"manual/advanced_graphics/color_palettes/#color-constants","title":"Color Constants","text":"

    PixelRoot32 provides predefined color constants:

    namespace pixelroot32::graphics {\n    Color::Black\n    Color::White\n    Color::Red\n    Color::Green\n    Color::Blue\n    Color::Yellow\n    Color::Cyan\n    Color::Magenta\n    Color::DarkGray\n    Color::LightGray\n    Color::Navy\n    Color::DarkGreen\n    Color::DarkRed\n    Color::Brown\n    Color::Purple\n    Color::Orange\n    Color::Pink\n    Color::Gold\n    Color::LightBlue\n    Color::LightGreen\n    Color::LightRed\n    Color::Transparent\n}\n
    "},{"location":"manual/advanced_graphics/color_palettes/#best-practices","title":"Best Practices","text":""},{"location":"manual/advanced_graphics/color_palettes/#palette-selection","title":"Palette Selection","text":"
    • Match game style: Choose palette that fits your game's theme
    • Test on hardware: Colors may look different on ESP32 display
    • Consider contrast: Ensure sprites are visible against background
    • Consistency: Stick with one palette per scene (or use dual mode)
    "},{"location":"manual/advanced_graphics/color_palettes/#dual-palette-mode_1","title":"Dual Palette Mode","text":"
    • Use sparingly: Not all games need dual palettes
    • Test combinations: Some palette combinations work better than others
    • Clear separation: Use for clear visual distinction between layers
    • Performance: No performance cost, use freely
    "},{"location":"manual/advanced_graphics/color_palettes/#custom-palettes_1","title":"Custom Palettes","text":"
    • Limit colors: Keep palette size reasonable (8-16 colors)
    • Plan ahead: Design palette before creating sprites
    • Test thoroughly: Verify colors work well together
    • Document: Comment your palette choices
    "},{"location":"manual/advanced_graphics/color_palettes/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/advanced_graphics/color_palettes/#palette-switching","title":"Palette Switching","text":"
    class GameScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Set initial palette\n        pixelroot32::graphics::setPalette(\n            pixelroot32::graphics::PaletteType::NES\n        );\n    }\n\n    void changeToNightMode() {\n        // Switch to darker palette\n        pixelroot32::graphics::setPalette(\n            pixelroot32::graphics::PaletteType::GB\n        );\n    }\n\n    void changeToDayMode() {\n        // Switch to brighter palette\n        pixelroot32::graphics::setPalette(\n            pixelroot32::graphics::PaletteType::PICO8\n        );\n    }\n};\n
    "},{"location":"manual/advanced_graphics/color_palettes/#theme-based-palettes","title":"Theme-Based Palettes","text":"
    namespace GamePalettes {\n    // Forest theme\n    static const pixelroot32::graphics::Color FOREST[] = {\n        Color::Black,\n        Color::DarkGreen,\n        Color::Green,\n        Color::LightGreen,\n        Color::Brown,\n        Color::Yellow\n    };\n\n    // Desert theme\n    static const pixelroot32::graphics::Color DESERT[] = {\n        Color::Black,\n        Color::Brown,\n        Color::Yellow,\n        Color::Gold,\n        Color::Orange,\n        Color::White\n    };\n\n    // Ocean theme\n    static const pixelroot32::graphics::Color OCEAN[] = {\n        Color::Black,\n        Color::Navy,\n        Color::Blue,\n        Color::Cyan,\n        Color::LightBlue,\n        Color::White\n    };\n}\n
    "},{"location":"manual/advanced_graphics/color_palettes/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/advanced_graphics/color_palettes/#colors-not-changing","title":"Colors Not Changing","text":"
    • Verify setPalette() is called before drawing
    • Check palette is set in init(), not update()
    • Ensure dual palette mode is enabled if using separate palettes
    • Verify Color constants are from correct namespace
    "},{"location":"manual/advanced_graphics/color_palettes/#colors-look-wrong-on-hardware","title":"Colors Look Wrong on Hardware","text":"
    • ESP32 displays may render colors differently
    • Test on actual hardware, not just PC
    • Adjust palette colors if needed
    • Consider display calibration
    "},{"location":"manual/advanced_graphics/color_palettes/#dual-palette-not-working","title":"Dual Palette Not Working","text":"
    • Ensure enableDualPaletteMode() is called first
    • Verify both palettes are set
    • Check that you're drawing in correct context
    • Review renderer documentation
    "},{"location":"manual/advanced_graphics/color_palettes/#next-steps","title":"Next Steps","text":"

    Now that you understand palettes, learn about: - Cameras and Scrolling - Create scrolling levels - Tilemaps - Build levels with tiles - Particles and Effects - Add visual effects

    See also: - API Reference - PaletteDefs - API Reference - Color - Manual - Basic Rendering

    "},{"location":"manual/advanced_graphics/particles_and_effects/","title":"Particles and Effects","text":"

    The particle system allows you to create visual effects like fire, explosions, smoke, and sparks. This guide covers ParticleEmitter, ParticleConfig, and the included presets.

    "},{"location":"manual/advanced_graphics/particles_and_effects/#particleemitter-basics","title":"ParticleEmitter Basics","text":"

    A ParticleEmitter is an Entity that manages a pool of particles to create visual effects.

    "},{"location":"manual/advanced_graphics/particles_and_effects/#creating-a-particle-emitter","title":"Creating a Particle Emitter","text":"
    #include <graphics/particles/ParticleEmitter.h>\n#include <graphics/particles/ParticleConfig.h>\n\n// Create particle configuration\npixelroot32::graphics::particles::ParticleConfig config;\nconfig.startColor = pixelroot32::graphics::Color::Red;\nconfig.endColor = pixelroot32::graphics::Color::Yellow;\nconfig.lifetime = 1.0f; // 1 second\nconfig.speed = 50.0f;\nconfig.gravity = -100.0f; // Upward (negative = up)\n\n// Create emitter\npixelroot32::graphics::particles::ParticleEmitter* emitter = \n    new pixelroot32::graphics::particles::ParticleEmitter(100, 100, config);\n\n// Add to scene\naddEntity(emitter);\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#emitting-particles","title":"Emitting Particles","text":"
    // Emit a burst of particles\nemitter->burst(100, 100, 10); // x, y, particle count\n\n// Particles will automatically update and draw\n// No additional code needed!\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#particleconfig","title":"ParticleConfig","text":"

    ParticleConfig defines how particles behave:

    #include <graphics/particles/ParticleConfig.h>\n\npixelroot32::graphics::particles::ParticleConfig config;\n\n// Colors\nconfig.startColor = pixelroot32::graphics::Color::Red;   // Color at spawn\nconfig.endColor = pixelroot32::graphics::Color::Yellow;  // Color at death\n\n// Lifetime\nconfig.lifetime = 0.5f; // Duration in seconds\n\n// Velocity\nconfig.speed = 100.0f;           // Base speed\nconfig.speedVariation = 20.0f;   // Random variation\nconfig.direction = 90.0f;        // Direction in degrees (0 = right, 90 = up)\nconfig.directionVariation = 45.0f; // Random direction spread\n\n// Physics\nconfig.gravity = 200.0f;  // Gravity force (positive = down)\nconfig.friction = 0.95f;   // Friction (0.0 to 1.0, 1.0 = no friction)\n\n// Size\nconfig.startSize = 2;     // Size at spawn (pixels)\nconfig.endSize = 1;       // Size at death\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#complete-config-example","title":"Complete Config Example","text":"
    pixelroot32::graphics::particles::ParticleConfig fireConfig;\n\n// Fire colors (red to yellow)\nfireConfig.startColor = pixelroot32::graphics::Color::Red;\nfireConfig.endColor = pixelroot32::graphics::Color::Yellow;\n\n// Short lifetime\nfireConfig.lifetime = 0.3f;\n\n// Upward movement with variation\nfireConfig.speed = 80.0f;\nfireConfig.speedVariation = 30.0f;\nfireConfig.direction = 90.0f; // Up\nfireConfig.directionVariation = 30.0f; // Spread\n\n// Upward gravity (negative)\nfireConfig.gravity = -50.0f;\n\n// Slight friction\nfireConfig.friction = 0.98f;\n\n// Size\nfireConfig.startSize = 3;\nfireConfig.endSize = 1;\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#built-in-presets","title":"Built-in Presets","text":"

    PixelRoot32 includes several particle presets for common effects:

    "},{"location":"manual/advanced_graphics/particles_and_effects/#fire","title":"Fire","text":"
    #include <graphics/particles/ParticlePresets.h>\n\n// Create fire emitter\npixelroot32::graphics::particles::ParticleEmitter* fire = \n    new pixelroot32::graphics::particles::ParticleEmitter(\n        100, 100,\n        pixelroot32::graphics::particles::ParticlePresets::Fire()\n    );\n\n// Emit continuous fire\nvoid update(unsigned long deltaTime) override {\n    fire->burst(100, 100, 2); // Emit 2 particles per frame\n    Scene::update(deltaTime);\n}\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#explosion","title":"Explosion","text":"
    // Create explosion emitter\npixelroot32::graphics::particles::ParticleEmitter* explosion = \n    new pixelroot32::graphics::particles::ParticleEmitter(\n        100, 100,\n        pixelroot32::graphics::particles::ParticlePresets::Explosion()\n    );\n\n// Emit explosion burst\nexplosion->burst(100, 100, 20); // 20 particles at once\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#sparks","title":"Sparks","text":"
    // Create sparks emitter\npixelroot32::graphics::particles::ParticleEmitter* sparks = \n    new pixelroot32::graphics::particles::ParticleEmitter(\n        100, 100,\n        pixelroot32::graphics::particles::ParticlePresets::Sparks()\n    );\n\n// Emit sparks\nsparks->burst(100, 100, 10);\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#smoke","title":"Smoke","text":"
    // Create smoke emitter\npixelroot32::graphics::particles::ParticleEmitter* smoke = \n    new pixelroot32::graphics::particles::ParticleEmitter(\n        100, 100,\n        pixelroot32::graphics::particles::ParticlePresets::Smoke()\n    );\n\n// Emit smoke\nsmoke->burst(100, 100, 3);\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#dust","title":"Dust","text":"
    // Create dust emitter\npixelroot32::graphics::particles::ParticleEmitter* dust = \n    new pixelroot32::graphics::particles::ParticleEmitter(\n        100, 100,\n        pixelroot32::graphics::particles::ParticlePresets::Dust()\n    );\n\n// Emit dust\ndust->burst(100, 100, 5);\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#complete-example-explosion-effect","title":"Complete Example: Explosion Effect","text":"
    #include <core/Scene.h>\n#include <graphics/particles/ParticleEmitter.h>\n#include <graphics/particles/ParticlePresets.h>\n\nclass ExplosionEffect : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::particles::ParticleEmitter* explosion;\n    bool active = false;\n\npublic:\n    ExplosionEffect()\n        : Entity(0, 0, 1, 1, pixelroot32::core::EntityType::GENERIC) {\n        setRenderLayer(1);\n\n        // Create explosion emitter\n        explosion = new pixelroot32::graphics::particles::ParticleEmitter(\n            0, 0,\n            pixelroot32::graphics::particles::ParticlePresets::Explosion()\n        );\n    }\n\n    void trigger(float x, float y) {\n        active = true;\n        this->x = x;\n        this->y = y;\n\n        // Emit explosion burst\n        explosion->burst(x, y, 25);\n    }\n\n    void update(unsigned long deltaTime) override {\n        explosion->update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        explosion->draw(renderer);\n    }\n};\n\n// Usage in scene\nvoid MyScene::init() override {\n    explosionEffect = new ExplosionEffect();\n    addEntity(explosionEffect);\n}\n\nvoid MyScene::update(unsigned long deltaTime) override {\n    auto& input = engine.getInputManager();\n\n    // Trigger explosion on button press\n    if (input.isButtonPressed(4)) { // Button A\n        explosionEffect->trigger(player->x, player->y);\n    }\n\n    Scene::update(deltaTime);\n}\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#continuous-effects","title":"Continuous Effects","text":"

    For continuous effects like fire or smoke:

    class FireEffect : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::particles::ParticleEmitter* fire;\n    unsigned long emitTimer = 0;\n    const unsigned long EMIT_INTERVAL_MS = 50; // Emit every 50ms\n\npublic:\n    FireEffect(float x, float y)\n        : Entity(x, y, 1, 1, pixelroot32::core::EntityType::GENERIC) {\n        setRenderLayer(1);\n\n        fire = new pixelroot32::graphics::particles::ParticleEmitter(\n            x, y,\n            pixelroot32::graphics::particles::ParticlePresets::Fire()\n        );\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Emit particles continuously\n        emitTimer += deltaTime;\n        if (emitTimer >= EMIT_INTERVAL_MS) {\n            emitTimer -= EMIT_INTERVAL_MS;\n            fire->burst(x, y, 2); // 2 particles per interval\n        }\n\n        fire->update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        fire->draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#custom-particle-effects","title":"Custom Particle Effects","text":"

    Create your own particle effects by customizing ParticleConfig:

    "},{"location":"manual/advanced_graphics/particles_and_effects/#magic-spell-effect","title":"Magic Spell Effect","text":"
    pixelroot32::graphics::particles::ParticleConfig magicConfig;\n\n// Magical colors (purple to cyan)\nmagicConfig.startColor = pixelroot32::graphics::Color::Purple;\nmagicConfig.endColor = pixelroot32::graphics::Color::Cyan;\n\n// Medium lifetime\nmagicConfig.lifetime = 0.8f;\n\n// Outward spread\nmagicConfig.speed = 60.0f;\nmagicConfig.speedVariation = 20.0f;\nmagicConfig.direction = 0.0f; // Right\nmagicConfig.directionVariation = 360.0f; // Full circle\n\n// Slight upward float\nmagicConfig.gravity = -30.0f;\n\n// Low friction (floaty)\nmagicConfig.friction = 0.92f;\n\n// Size\nmagicConfig.startSize = 2;\nmagicConfig.endSize = 1;\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#rain-effect","title":"Rain Effect","text":"
    pixelroot32::graphics::particles::ParticleConfig rainConfig;\n\n// Rain color (light blue)\nrainConfig.startColor = pixelroot32::graphics::Color::LightBlue;\nrainConfig.endColor = pixelroot32::graphics::Color::LightBlue;\n\n// Long lifetime\nrainConfig.lifetime = 2.0f;\n\n// Downward movement\nrainConfig.speed = 150.0f;\nrainConfig.speedVariation = 20.0f;\nrainConfig.direction = 270.0f; // Down\nrainConfig.directionVariation = 5.0f; // Slight angle variation\n\n// Downward gravity\nrainConfig.gravity = 200.0f;\n\n// No friction\nrainConfig.friction = 1.0f;\n\n// Small size\nrainConfig.startSize = 1;\nrainConfig.endSize = 1;\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#best-practices","title":"Best Practices","text":""},{"location":"manual/advanced_graphics/particles_and_effects/#performance","title":"Performance","text":"
    • Limit particle count: Each emitter has MAX_PARTICLES_PER_EMITTER (50)
    • Reuse emitters: Don't create new emitters every frame
    • Disable when not visible: Set isVisible = false when off-screen
    • Limit active emitters: Too many emitters can impact performance
    "},{"location":"manual/advanced_graphics/particles_and_effects/#visual-design","title":"Visual Design","text":"
    • Match game style: Particle effects should fit your game's aesthetic
    • Use appropriate colors: Match particle colors to game palette
    • Test on hardware: ESP32 may render particles differently
    • Keep it simple: Simple effects often look better than complex ones
    "},{"location":"manual/advanced_graphics/particles_and_effects/#timing","title":"Timing","text":"
    • Burst timing: Space out bursts for better visual effect
    • Continuous effects: Use timers to control emission rate
    • Lifetime: Adjust lifetime to match effect duration
    • Cleanup: Particles automatically clean up when lifetime expires
    "},{"location":"manual/advanced_graphics/particles_and_effects/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/advanced_graphics/particles_and_effects/#one-shot-effect","title":"One-Shot Effect","text":"
    class OneShotEffect : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::particles::ParticleEmitter* emitter;\n    bool hasEmitted = false;\n\npublic:\n    void trigger(float x, float y) {\n        if (!hasEmitted) {\n            emitter->burst(x, y, 20);\n            hasEmitted = true;\n        }\n    }\n\n    void reset() {\n        hasEmitted = false;\n    }\n};\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#attached-effect","title":"Attached Effect","text":"
    class AttachedParticleEffect : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::particles::ParticleEmitter* emitter;\n    pixelroot32::core::Actor* target;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        // Update emitter position to follow target\n        emitter->x = target->x;\n        emitter->y = target->y;\n\n        // Emit particles\n        emitter->burst(target->x, target->y, 1);\n\n        emitter->update(deltaTime);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/particles_and_effects/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/advanced_graphics/particles_and_effects/#particles-not-appearing","title":"Particles Not Appearing","text":"
    • Verify emitter is added to scene
    • Check particle config is valid
    • Ensure burst() is being called
    • Verify emitter position is on-screen
    "},{"location":"manual/advanced_graphics/particles_and_effects/#performance-issues","title":"Performance Issues","text":"
    • Reduce particle count per burst
    • Limit number of active emitters
    • Use simpler particle configs
    • Disable emitters when not visible
    "},{"location":"manual/advanced_graphics/particles_and_effects/#particles-not-moving","title":"Particles Not Moving","text":"
    • Check gravity value (positive = down, negative = up)
    • Verify speed is not 0
    • Check friction isn't too high (1.0 = no movement)
    • Ensure direction is correct (degrees: 0=right, 90=up, 180=left, 270=down)
    "},{"location":"manual/advanced_graphics/particles_and_effects/#next-steps","title":"Next Steps","text":"

    Now that you understand particles, you've completed the advanced graphics section. Continue with: - Performance Optimization - Optimize your game - Memory Management - Manage memory efficiently - API Reference - Complete API documentation

    See also: - API Reference - ParticleEmitter - API Reference - ParticleConfig - API Reference - ParticlePresets - Manual - Basic Rendering

    "},{"location":"manual/advanced_graphics/sprites_and_animation/","title":"Sprites and Animation","text":"

    This guide covers advanced sprite techniques and animation in PixelRoot32, including different sprite formats, creating animations, and best practices.

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#sprite-formats","title":"Sprite Formats","text":"

    PixelRoot32 supports multiple sprite formats, each optimized for different use cases.

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#1bpp-standard-monochrome","title":"1bpp (Standard, Monochrome)","text":"

    The standard format uses 1 bit per pixel (monochrome). This is the most memory-efficient format:

    #include <graphics/Renderer.h>\n\n// Define sprite data (8x8 example)\nstatic const uint16_t PLAYER_SPRITE_DATA[] = {\n    0b00111100,  // Row 0\n    0b01111110,  // Row 1\n    0b11111111,  // Row 2\n    0b11111111,  // Row 3\n    0b11111111,  // Row 4\n    0b01111110,  // Row 5\n    0b00111100,  // Row 6\n    0b00000000   // Row 7\n};\n\n// Create sprite descriptor\nstatic const pixelroot32::graphics::Sprite PLAYER_SPRITE = {\n    PLAYER_SPRITE_DATA,\n    8,  // width\n    8   // height\n};\n\n// Draw sprite\nrenderer.drawSprite(PLAYER_SPRITE, 100, 100, pixelroot32::graphics::Color::White);\n

    Characteristics: - Most memory-efficient - 1 bit per pixel - Maximum width: 16 pixels - Color applied at draw time - Best for: Simple graphics, retro style

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#2bpp-experimental-4-colors","title":"2bpp (Experimental, 4 Colors)","text":"

    2 bits per pixel allows 4 colors per sprite:

    #ifdef PIXELROOT32_ENABLE_2BPP_SPRITES\n#include <graphics/Renderer.h>\n\n// Define 2bpp sprite data\nstatic const uint8_t COLORFUL_SPRITE_DATA[] = {\n    // Each byte represents 4 pixels (2 bits each)\n    // Format: [pixel3][pixel2][pixel1][pixel0]\n    0x00, 0x11, 0x22, 0x33,  // Row 0\n    0x11, 0x22, 0x33, 0x00,  // Row 1\n    // ... more rows\n};\n\n// Define palette (4 colors)\nstatic const pixelroot32::graphics::Color SPRITE_PALETTE[] = {\n    pixelroot32::graphics::Color::Transparent,\n    pixelroot32::graphics::Color::Red,\n    pixelroot32::graphics::Color::Green,\n    pixelroot32::graphics::Color::Blue\n};\n\n// Create 2bpp sprite\nstatic const pixelroot32::graphics::Sprite2bpp COLORFUL_SPRITE = {\n    COLORFUL_SPRITE_DATA,\n    SPRITE_PALETTE,\n    16,  // width\n    8,   // height\n    4    // palette size\n};\n\n// Draw 2bpp sprite\nrenderer.drawSprite(COLORFUL_SPRITE, 100, 100, false);\n#endif\n

    Characteristics: - 2 bits per pixel (4 colors) - Requires custom palette - More memory than 1bpp - Best for: More colorful sprites without full color

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#4bpp-experimental-16-colors","title":"4bpp (Experimental, 16 Colors)","text":"

    4 bits per pixel allows 16 colors per sprite:

    #ifdef PIXELROOT32_ENABLE_4BPP_SPRITES\n#include <graphics/Renderer.h>\n\n// Define 4bpp sprite data\nstatic const uint8_t RICH_SPRITE_DATA[] = {\n    // Each byte represents 2 pixels (4 bits each)\n    // Format: [pixel1][pixel0]\n    0x01, 0x23, 0x45, 0x67,  // Row 0\n    // ... more rows\n};\n\n// Define palette (16 colors)\nstatic const pixelroot32::graphics::Color RICH_PALETTE[] = {\n    pixelroot32::graphics::Color::Transparent,\n    pixelroot32::graphics::Color::Black,\n    pixelroot32::graphics::Color::DarkGray,\n    // ... 13 more colors\n};\n\n// Create 4bpp sprite\nstatic const pixelroot32::graphics::Sprite4bpp RICH_SPRITE = {\n    RICH_SPRITE_DATA,\n    RICH_PALETTE,\n    16,  // width\n    16,  // height\n    16   // palette size\n};\n\n// Draw 4bpp sprite\nrenderer.drawSprite(RICH_SPRITE, 100, 100, false);\n#endif\n

    Characteristics: - 4 bits per pixel (16 colors) - Requires custom palette - Most memory-intensive - Best for: Detailed sprites with many colors

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#multisprite-multi-layer","title":"MultiSprite (Multi-Layer)","text":"

    MultiSprite combines multiple 1bpp layers to create multi-color sprites:

    #include <graphics/Renderer.h>\n\n// Define layers (each is 1bpp)\nstatic const uint16_t BASE_LAYER_DATA[] = {\n    0b00111100,\n    0b01111110,\n    0b11111111,\n    0b11111111,\n    0b11111111,\n    0b01111110,\n    0b00111100,\n    0b00000000\n};\n\nstatic const uint16_t HIGHLIGHT_LAYER_DATA[] = {\n    0b00000000,\n    0b00011000,\n    0b00111100,\n    0b00111100,\n    0b00111100,\n    0b00011000,\n    0b00000000,\n    0b00000000\n};\n\n// Create layers\nstatic const pixelroot32::graphics::SpriteLayer LAYERS[] = {\n    { BASE_LAYER_DATA, pixelroot32::graphics::Color::Blue },      // Base layer\n    { HIGHLIGHT_LAYER_DATA, pixelroot32::graphics::Color::Cyan }  // Highlight layer\n};\n\n// Create MultiSprite\nstatic const pixelroot32::graphics::MultiSprite PLAYER_MULTI = {\n    8,      // width\n    8,      // height\n    LAYERS, // layers array\n    2       // layer count\n};\n\n// Draw MultiSprite\nrenderer.drawSprite(PLAYER_MULTI, 100, 100, false);\n

    Characteristics: - Combines multiple 1bpp layers - Each layer can have different color - Layers drawn in order (first = bottom) - Best for: Complex sprites with highlights, outlines, etc.

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#creating-sprites","title":"Creating Sprites","text":""},{"location":"manual/advanced_graphics/sprites_and_animation/#manual-creation-1bpp","title":"Manual Creation (1bpp)","text":"

    For simple sprites, you can create them manually:

    // 8x8 sprite: Simple circle\nstatic const uint16_t CIRCLE_SPRITE_DATA[] = {\n    0b00111100,  //   ####\n    0b01111110,  //  ######\n    0b11111111,  // ########\n    0b11111111,  // ########\n    0b11111111,  // ########\n    0b11111111,  // ########\n    0b01111110,  //  ######\n    0b00111100   //   ####\n};\n\nstatic const pixelroot32::graphics::Sprite CIRCLE_SPRITE = {\n    CIRCLE_SPRITE_DATA,\n    8,\n    8\n};\n

    Tips: - Use binary notation for clarity - Comment each row to visualize - Keep sprites small (8x8, 16x16) - Reuse sprites when possible

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#using-sprite-compiler","title":"Using Sprite Compiler","text":"

    For complex sprites, use the Sprite Compiler tool (if available) to convert PNG images to sprite data.

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#sprite-animation","title":"Sprite Animation","text":"

    PixelRoot32 uses a step-based animation system that's lightweight and efficient.

    "},{"location":"manual/advanced_graphics/sprites_and_animation/#spriteanimation-structure","title":"SpriteAnimation Structure","text":"
    #include <graphics/Renderer.h>\n\n// Define animation frames\nstatic const uint16_t FRAME1_DATA[] = { /* ... */ };\nstatic const uint16_t FRAME2_DATA[] = { /* ... */ };\nstatic const uint16_t FRAME3_DATA[] = { /* ... */ };\n\nstatic const pixelroot32::graphics::Sprite FRAME1 = { FRAME1_DATA, 8, 8 };\nstatic const pixelroot32::graphics::Sprite FRAME2 = { FRAME2_DATA, 8, 8 };\nstatic const pixelroot32::graphics::Sprite FRAME3 = { FRAME3_DATA, 8, 8 };\n\n// Create animation frames\nstatic const pixelroot32::graphics::SpriteAnimationFrame ANIMATION_FRAMES[] = {\n    { &FRAME1, nullptr },  // Frame 1 (no mask)\n    { &FRAME2, nullptr },  // Frame 2\n    { &FRAME3, nullptr }   // Frame 3\n};\n\n// Create animation\npixelroot32::graphics::SpriteAnimation walkAnimation;\nwalkAnimation.frames = ANIMATION_FRAMES;\nwalkAnimation.frameCount = 3;\nwalkAnimation.current = 0;\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#using-animations-in-actors","title":"Using Animations in Actors","text":"
    #include <core/Actor.h>\n#include <graphics/Renderer.h>\n\nclass AnimatedPlayer : public pixelroot32::core::Actor {\nprivate:\n    pixelroot32::graphics::SpriteAnimation walkAnimation;\n    unsigned long animationTimer = 0;\n    const unsigned long FRAME_DURATION_MS = 100; // 100ms per frame\n\npublic:\n    AnimatedPlayer(float x, float y)\n        : Actor(x, y, 8, 8) {\n        setRenderLayer(1);\n\n        // Initialize animation\n        walkAnimation.frames = WALK_ANIMATION_FRAMES;\n        walkAnimation.frameCount = 3;\n        walkAnimation.current = 0;\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Update animation\n        animationTimer += deltaTime;\n        if (animationTimer >= FRAME_DURATION_MS) {\n            animationTimer -= FRAME_DURATION_MS;\n            walkAnimation.step(); // Advance to next frame\n        }\n\n        // Movement logic...\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Get current frame\n        const pixelroot32::graphics::Sprite* currentFrame = \n            walkAnimation.frames[walkAnimation.current].sprite;\n\n        // Draw current frame\n        renderer.drawSprite(\n            *currentFrame,\n            static_cast<int>(x),\n            static_cast<int>(y),\n            pixelroot32::graphics::Color::White,\n            false // flipX\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Handle collision\n    }\n};\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#animation-control","title":"Animation Control","text":"
    // Reset animation to first frame\nwalkAnimation.reset();\n\n// Step to next frame (loops automatically)\nwalkAnimation.step();\n\n// Get current frame\nconst pixelroot32::graphics::Sprite* frame = \n    walkAnimation.frames[walkAnimation.current].sprite;\n\n// Check if animation is at specific frame\nif (walkAnimation.current == 0) {\n    // At first frame\n}\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#multiple-animations","title":"Multiple Animations","text":"

    For actors with multiple animations (idle, walk, jump):

    class PlayerActor : public pixelroot32::core::Actor {\nprivate:\n    enum class AnimationState {\n        IDLE,\n        WALK,\n        JUMP\n    };\n\n    AnimationState currentState = AnimationState::IDLE;\n    pixelroot32::graphics::SpriteAnimation idleAnim;\n    pixelroot32::graphics::SpriteAnimation walkAnim;\n    pixelroot32::graphics::SpriteAnimation jumpAnim;\n\n    pixelroot32::graphics::SpriteAnimation* getCurrentAnimation() {\n        switch (currentState) {\n            case AnimationState::IDLE: return &idleAnim;\n            case AnimationState::WALK: return &walkAnim;\n            case AnimationState::JUMP: return &jumpAnim;\n        }\n        return &idleAnim;\n    }\n\npublic:\n    void update(unsigned long deltaTime) override {\n        // Update current animation\n        auto* anim = getCurrentAnimation();\n        animationTimer += deltaTime;\n        if (animationTimer >= FRAME_DURATION_MS) {\n            animationTimer -= FRAME_DURATION_MS;\n            anim->step();\n        }\n\n        // Change animation state based on game logic\n        if (isMoving) {\n            if (currentState != AnimationState::WALK) {\n                currentState = AnimationState::WALK;\n                walkAnim.reset();\n            }\n        } else if (isJumping) {\n            if (currentState != AnimationState::JUMP) {\n                currentState = AnimationState::JUMP;\n                jumpAnim.reset();\n            }\n        } else {\n            if (currentState != AnimationState::IDLE) {\n                currentState = AnimationState::IDLE;\n                idleAnim.reset();\n            }\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        auto* anim = getCurrentAnimation();\n        const auto* frame = anim->frames[anim->current].sprite;\n        renderer.drawSprite(*frame, static_cast<int>(x), static_cast<int>(y), \n            pixelroot32::graphics::Color::White);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#sprite-flipping","title":"Sprite Flipping","text":"

    Flip sprites horizontally for facing direction:

    bool facingRight = true;\n\nvoid draw(pixelroot32::graphics::Renderer& renderer) override {\n    renderer.drawSprite(\n        PLAYER_SPRITE,\n        static_cast<int>(x),\n        static_cast<int>(y),\n        pixelroot32::graphics::Color::White,\n        !facingRight // Flip if facing left\n    );\n}\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#best-practices","title":"Best Practices","text":""},{"location":"manual/advanced_graphics/sprites_and_animation/#memory-optimization","title":"Memory Optimization","text":"
    • Reuse sprites: Define sprites once, use many times
    • Use 1bpp when possible: Most memory-efficient
    • Store in flash: Use static const to keep in flash memory
    • Limit sprite count: Too many unique sprites can exhaust memory
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#performance","title":"Performance","text":"
    • Pre-calculate animations: Set up animations in init(), not update()
    • Limit active animations: Only animate visible entities
    • Use appropriate formats: Don't use 4bpp if 1bpp works
    • Batch similar sprites: Draw similar sprites together
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#organization","title":"Organization","text":"
    • Group related sprites: Keep sprite data together
    • Use meaningful names: Name sprites clearly
    • Document complex sprites: Comment sprite bit patterns
    • Create sprite libraries: Reusable sprite collections
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/advanced_graphics/sprites_and_animation/#sprite-sheet-pattern","title":"Sprite Sheet Pattern","text":"

    Organize multiple sprites in arrays:

    namespace PlayerSprites {\n    static const uint16_t IDLE_FRAME1[] = { /* ... */ };\n    static const uint16_t IDLE_FRAME2[] = { /* ... */ };\n    static const uint16_t WALK_FRAME1[] = { /* ... */ };\n    static const uint16_t WALK_FRAME2[] = { /* ... */ };\n\n    static const pixelroot32::graphics::Sprite IDLE[] = {\n        { IDLE_FRAME1, 8, 8 },\n        { IDLE_FRAME2, 8, 8 }\n    };\n\n    static const pixelroot32::graphics::Sprite WALK[] = {\n        { WALK_FRAME1, 8, 8 },\n        { WALK_FRAME2, 8, 8 }\n    };\n}\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#animation-helper","title":"Animation Helper","text":"

    Create a helper class for animation management:

    class AnimationController {\nprivate:\n    pixelroot32::graphics::SpriteAnimation* currentAnim;\n    unsigned long timer = 0;\n    unsigned long frameDuration;\n\npublic:\n    void setAnimation(pixelroot32::graphics::SpriteAnimation* anim) {\n        if (currentAnim != anim) {\n            currentAnim = anim;\n            currentAnim->reset();\n            timer = 0;\n        }\n    }\n\n    void update(unsigned long deltaTime) {\n        if (currentAnim) {\n            timer += deltaTime;\n            if (timer >= frameDuration) {\n                timer -= frameDuration;\n                currentAnim->step();\n            }\n        }\n    }\n\n    const pixelroot32::graphics::Sprite* getCurrentFrame() {\n        if (currentAnim && currentAnim->frameCount > 0) {\n            return currentAnim->frames[currentAnim->current].sprite;\n        }\n        return nullptr;\n    }\n};\n
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/advanced_graphics/sprites_and_animation/#sprites-not-displaying","title":"Sprites Not Displaying","text":"
    • Check sprite data is valid
    • Verify width/height match data
    • Ensure sprite is within screen bounds
    • Check render layer is correct
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#animation-not-playing","title":"Animation Not Playing","text":"
    • Verify animation frames are set
    • Check step() is being called
    • Ensure timer logic is correct
    • Verify frame count matches array size
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#memory-issues","title":"Memory Issues","text":"
    • Reduce sprite count
    • Use 1bpp instead of 2bpp/4bpp
    • Reuse sprites more
    • Check available flash memory
    "},{"location":"manual/advanced_graphics/sprites_and_animation/#next-steps","title":"Next Steps","text":"

    Now that you understand sprites and animation, learn about: - Color Palettes - Use different color schemes - Cameras and Scrolling - Create scrolling levels - Tilemaps - Build levels with tiles

    See also: - API Reference - Sprite - API Reference - SpriteAnimation - Manual - Basic Rendering

    "},{"location":"manual/advanced_graphics/tilemaps/","title":"Tilemaps","text":"

    Tilemaps allow you to build levels efficiently by reusing small tile sprites. This guide covers creating tilemaps, rendering them, and using them with scrolling cameras.

    "},{"location":"manual/advanced_graphics/tilemaps/#what-are-tilemaps","title":"What are Tilemaps?","text":"

    A tilemap is a 2D grid where each cell references a tile sprite. Instead of placing individual sprites, you define which tile appears at each grid position.

    Advantages: - Memory efficient: Reuse tile sprites many times - Easy level design: Edit level data, not code - Fast rendering: Optimized tilemap drawing - Large levels: Create levels bigger than screen - Multiple Bit-Depths: Support for 1bpp, 2bpp, and 4bpp tilemaps for higher graphical fidelity

    "},{"location":"manual/advanced_graphics/tilemaps/#creating-a-tilemap","title":"Creating a Tilemap","text":""},{"location":"manual/advanced_graphics/tilemaps/#1-define-tiles","title":"1. Define Tiles","text":"

    First, create the tile sprites you'll reuse. You can use standard 1bpp sprites or multi-bpp sprites (2bpp/4bpp) if enabled.

    "},{"location":"manual/advanced_graphics/tilemaps/#1bpp-tiles-example","title":"1bpp Tiles Example","text":"
    #include <graphics/Renderer.h>\n\n// Ground tile (solid)\nstatic const uint16_t TILE_GROUND_BITS[] = {\n    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,\n    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF\n};\n\n// Create tile sprites (8x8 tiles)\nstatic const pixelroot32::graphics::Sprite TILES[] = {\n    { TILE_EMPTY_BITS, 8, 8 },  // Index 0: Empty\n    { TILE_GROUND_BITS, 8, 8 }  // Index 1: Ground\n};\n
    "},{"location":"manual/advanced_graphics/tilemaps/#2bpp-tiles-example-multi-color","title":"2bpp Tiles Example (Multi-color)","text":"
    #include <graphics/Renderer.h>\n\n// 2bpp grass tile\nstatic const uint8_t TILE_GRASS_DATA[] = {\n    0b01010101, 0b01010101, // Packed 2bpp data\n    // ...\n};\n\nstatic const Color GRASS_PALETTE[] = {\n    Color::Transparent, Color::DarkGreen, Color::Green, Color::LightGreen\n};\n\nstatic const pixelroot32::graphics::Sprite2bpp TILES_2BPP[] = {\n    { TILE_GRASS_DATA, GRASS_PALETTE, 8, 8, 4 }\n};\n
    "},{"location":"manual/advanced_graphics/tilemaps/#2-create-tile-index-array","title":"2. Create Tile Index Array","text":"

    Define which tile appears at each position:

    // Tilemap dimensions (30 tiles wide, 20 tiles tall)\nstatic const int TILEMAP_WIDTH = 30;\nstatic const int TILEMAP_HEIGHT = 20;\n\n// Array of tile indices (each byte is a tile index)\nstatic uint8_t TILEMAP_INDICES[TILEMAP_WIDTH * TILEMAP_HEIGHT];\n\n// Initialize to empty\nvoid initTilemap() {\n    for (int i = 0; i < TILEMAP_WIDTH * TILEMAP_HEIGHT; i++) {\n        TILEMAP_INDICES[i] = 0; // Empty\n    }\n\n    // Draw ground at bottom\n    int groundRow = TILEMAP_HEIGHT - 1;\n    for (int x = 0; x < TILEMAP_WIDTH; x++) {\n        TILEMAP_INDICES[groundRow * TILEMAP_WIDTH + x] = 1; // Ground tile\n    }\n\n    // Add some walls\n    TILEMAP_INDICES[5 * TILEMAP_WIDTH + 10] = 2; // Wall at (10, 5)\n    TILEMAP_INDICES[5 * TILEMAP_WIDTH + 11] = 2;\n    TILEMAP_INDICES[5 * TILEMAP_WIDTH + 12] = 2;\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#3-create-tilemap-structure","title":"3. Create TileMap Structure","text":"
    #include <graphics/Renderer.h>\n\nstatic pixelroot32::graphics::TileMap myTileMap = {\n    TILEMAP_INDICES,                    // indices array\n    TILEMAP_WIDTH,                      // width (in tiles)\n    TILEMAP_HEIGHT,                     // height (in tiles)\n    TILES,                              // tiles array\n    8,                                  // tile width (pixels)\n    8,                                  // tile height (pixels)\n    sizeof(TILES) / sizeof(pixelroot32::graphics::Sprite) // tile count\n};\n
    "},{"location":"manual/advanced_graphics/tilemaps/#rendering-tilemaps","title":"Rendering Tilemaps","text":""},{"location":"manual/advanced_graphics/tilemaps/#basic-rendering","title":"Basic Rendering","text":"
    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // 1bpp Tilemap (requires a color)\n    renderer.drawTileMap(\n        myTileMap,\n        0, 0,\n        pixelroot32::graphics::Color::White\n    );\n\n    // 2bpp/4bpp Tilemap (colors are in the sprite palettes)\n    renderer.drawTileMap(myTileMap2bpp, 0, 100);\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#with-camerascrolling","title":"With Camera/Scrolling","text":"
    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Apply camera first\n    camera.apply(renderer);\n\n    // Draw tilemap (camera offset is automatically applied)\n    renderer.drawTileMap(\n        myTileMap,\n        0,                              // World position (0, 0)\n        0,\n        pixelroot32::graphics::Color::White\n    );\n\n    // Draw game objects\n    Scene::draw(renderer);\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#complete-example-platformer-level","title":"Complete Example: Platformer Level","text":"
    #include <core/Scene.h>\n#include <graphics/Renderer.h>\n#include <graphics/Camera2D.h>\n\nclass PlatformerLevel : public pixelroot32::core::Scene {\nprivate:\n    static const int TILE_SIZE = 8;\n    static const int TILEMAP_WIDTH = 100;  // 800 pixels wide\n    static const int TILEMAP_HEIGHT = 30;   // 240 pixels tall\n\n    // Tile definitions\n    static const uint16_t TILE_EMPTY_BITS[] = {\n        0x0000, 0x0000, 0x0000, 0x0000,\n        0x0000, 0x0000, 0x0000, 0x0000\n    };\n\n    static const uint16_t TILE_GROUND_BITS[] = {\n        0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,\n        0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF\n    };\n\n    static const uint16_t TILE_GRASS_BITS[] = {\n        0x0000, 0x0000, 0x0000, 0x0000,\n        0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF\n    };\n\n    static const pixelroot32::graphics::Sprite TILES[] = {\n        { TILE_EMPTY_BITS, TILE_SIZE, TILE_SIZE },  // 0: Empty\n        { TILE_GROUND_BITS, TILE_SIZE, TILE_SIZE }, // 1: Ground\n        { TILE_GRASS_BITS, TILE_SIZE, TILE_SIZE }   // 2: Grass top\n    };\n\n    static uint8_t LEVEL_INDICES[TILEMAP_WIDTH * TILEMAP_HEIGHT];\n\n    pixelroot32::graphics::TileMap levelTileMap;\n    pixelroot32::graphics::Camera2D camera;\n\npublic:\n    PlatformerLevel() \n        : camera(240, 240) {\n        // Initialize tilemap structure\n        levelTileMap = {\n            LEVEL_INDICES,\n            TILEMAP_WIDTH,\n            TILEMAP_HEIGHT,\n            TILES,\n            TILE_SIZE,\n            TILE_SIZE,\n            sizeof(TILES) / sizeof(pixelroot32::graphics::Sprite)\n        };\n    }\n\n    void init() override {\n        // Initialize all tiles to empty\n        for (int i = 0; i < TILEMAP_WIDTH * TILEMAP_HEIGHT; i++) {\n            LEVEL_INDICES[i] = 0;\n        }\n\n        // Create ground level\n        int groundY = TILEMAP_HEIGHT - 1;\n        for (int x = 0; x < TILEMAP_WIDTH; x++) {\n            LEVEL_INDICES[groundY * TILEMAP_WIDTH + x] = 1; // Ground\n        }\n\n        // Add grass on top of ground\n        int grassY = groundY - 1;\n        for (int x = 0; x < TILEMAP_WIDTH; x++) {\n            LEVEL_INDICES[grassY * TILEMAP_WIDTH + x] = 2; // Grass\n        }\n\n        // Add platforms\n        // Platform 1: x=10 to x=15, y=20\n        for (int x = 10; x < 16; x++) {\n            LEVEL_INDICES[20 * TILEMAP_WIDTH + x] = 1; // Ground tile\n        }\n\n        // Platform 2: x=30 to x=35, y=15\n        for (int x = 30; x < 36; x++) {\n            LEVEL_INDICES[15 * TILEMAP_WIDTH + x] = 1;\n        }\n\n        // Set camera boundaries\n        camera.setBounds(0, TILEMAP_WIDTH * TILE_SIZE - 240);\n        camera.setVerticalBounds(0, TILEMAP_HEIGHT * TILE_SIZE - 240);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Follow player (example)\n        // camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Apply camera\n        camera.apply(renderer);\n\n        // Draw tilemap\n        renderer.drawTileMap(\n            levelTileMap,\n            0, 0,\n            pixelroot32::graphics::Color::White\n        );\n\n        // Draw game objects\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/advanced_graphics/tilemaps/#tilemap-with-scroll","title":"Tilemap with Scroll","text":"

    For scrolling levels, combine tilemaps with cameras:

    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // Apply camera (handles scrolling)\n    camera.apply(renderer);\n\n    // Draw tilemap (automatically scrolled by camera)\n    renderer.drawTileMap(\n        levelTileMap,\n        0, 0,\n        pixelroot32::graphics::Color::White\n    );\n\n    // Draw entities (also scrolled)\n    Scene::draw(renderer);\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#optimizing-tilemap-rendering","title":"Optimizing Tilemap Rendering","text":""},{"location":"manual/advanced_graphics/tilemaps/#viewport-culling","title":"Viewport Culling","text":"

    Only draw visible tiles:

    void drawTileMapOptimized(\n    pixelroot32::graphics::Renderer& renderer,\n    const pixelroot32::graphics::TileMap& tileMap,\n    int offsetX, int offsetY,\n    pixelroot32::graphics::Color color\n) {\n    int screenWidth = renderer.getWidth();\n    int screenHeight = renderer.getHeight();\n\n    // Calculate which tiles are visible\n    int startTileX = (offsetX < 0) ? (-offsetX / tileMap.tileWidth) : 0;\n    int startTileY = (offsetY < 0) ? (-offsetY / tileMap.tileHeight) : 0;\n    int endTileX = startTileX + (screenWidth / tileMap.tileWidth) + 1;\n    int endTileY = startTileY + (screenHeight / tileMap.tileHeight) + 1;\n\n    // Clamp to tilemap bounds\n    if (startTileX < 0) startTileX = 0;\n    if (startTileY < 0) startTileY = 0;\n    if (endTileX > tileMap.width) endTileX = tileMap.width;\n    if (endTileY > tileMap.height) endTileY = tileMap.height;\n\n    // Draw only visible tiles\n    for (int ty = startTileY; ty < endTileY; ty++) {\n        for (int tx = startTileX; tx < endTileX; tx++) {\n            uint8_t tileIndex = tileMap.indices[ty * tileMap.width + tx];\n            if (tileIndex < tileMap.tileCount) {\n                int x = tx * tileMap.tileWidth + offsetX;\n                int y = ty * tileMap.tileHeight + offsetY;\n                renderer.drawSprite(\n                    tileMap.tiles[tileIndex],\n                    x, y,\n                    color,\n                    false\n                );\n            }\n        }\n    }\n}\n

    Note: The built-in drawTileMap() already performs viewport culling, so you typically don't need to implement this yourself.

    "},{"location":"manual/advanced_graphics/tilemaps/#best-practices","title":"Best Practices","text":""},{"location":"manual/advanced_graphics/tilemaps/#tile-design","title":"Tile Design","text":"
    • Keep tiles small: 8x8 or 16x16 pixels work best
    • Reuse tiles: Design tiles that can be used in multiple ways
    • Consistent style: All tiles should match visually
    • Limit tile count: Too many unique tiles uses more memory
    "},{"location":"manual/advanced_graphics/tilemaps/#level-design","title":"Level Design","text":"
    • Use indices efficiently: 0 = empty, 1+ = different tiles
    • Plan layout: Design level on paper/grid first
    • Test on hardware: Large tilemaps may impact performance
    • Optimize data: Use compact level data format
    "},{"location":"manual/advanced_graphics/tilemaps/#performance","title":"Performance","text":"
    • Limit tilemap size: Very large tilemaps can be slow
    • Use appropriate tile size: Smaller tiles = more tiles to draw
    • Combine with culling: Only draw visible area
    • Test scrolling: Ensure smooth scrolling performance
    "},{"location":"manual/advanced_graphics/tilemaps/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/advanced_graphics/tilemaps/#level-data-in-code","title":"Level Data in Code","text":"
    // Define level as 2D array (easier to read)\nstatic const uint8_t LEVEL_DATA[][TILEMAP_WIDTH] = {\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, // Ground\n};\n\n// Copy to tilemap indices\nvoid loadLevel() {\n    for (int y = 0; y < TILEMAP_HEIGHT; y++) {\n        for (int x = 0; x < TILEMAP_WIDTH; x++) {\n            TILEMAP_INDICES[y * TILEMAP_WIDTH + x] = LEVEL_DATA[y][x];\n        }\n    }\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#collision-detection-with-tilemaps","title":"Collision Detection with Tilemaps","text":"
    bool isTileSolid(int tileX, int tileY) {\n    if (tileX < 0 || tileX >= TILEMAP_WIDTH ||\n        tileY < 0 || tileY >= TILEMAP_HEIGHT) {\n        return true; // Out of bounds = solid\n    }\n\n    uint8_t tileIndex = TILEMAP_INDICES[tileY * TILEMAP_WIDTH + tileX];\n    return tileIndex != 0; // 0 = empty, others = solid\n}\n\nbool checkCollision(float x, float y, int width, int height) {\n    // Convert world position to tile coordinates\n    int tileX1 = static_cast<int>(x) / TILE_SIZE;\n    int tileY1 = static_cast<int>(y) / TILE_SIZE;\n    int tileX2 = static_cast<int>(x + width) / TILE_SIZE;\n    int tileY2 = static_cast<int>(y + height) / TILE_SIZE;\n\n    // Check all tiles actor overlaps\n    for (int ty = tileY1; ty <= tileY2; ty++) {\n        for (int tx = tileX1; tx <= tileX2; tx++) {\n            if (isTileSolid(tx, ty)) {\n                return true; // Collision!\n            }\n        }\n    }\n\n    return false; // No collision\n}\n
    "},{"location":"manual/advanced_graphics/tilemaps/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/advanced_graphics/tilemaps/#tiles-not-appearing","title":"Tiles Not Appearing","text":"
    • Verify tile indices are correct (0 = first tile, 1 = second, etc.)
    • Check tilemap dimensions match indices array size
    • Ensure tiles array has enough entries
    • Verify tile size matches sprite size
    "},{"location":"manual/advanced_graphics/tilemaps/#performance-issues","title":"Performance Issues","text":"
    • Reduce tilemap size
    • Use smaller tiles
    • Limit number of unique tiles
    • Test viewport culling
    "},{"location":"manual/advanced_graphics/tilemaps/#scrolling-problems","title":"Scrolling Problems","text":"
    • Ensure camera is applied before drawing tilemap
    • Check tilemap position matches camera offset
    • Verify tilemap boundaries are correct
    • Test with simple tilemap first
    "},{"location":"manual/advanced_graphics/tilemaps/#next-steps","title":"Next Steps","text":"

    Now that you understand tilemaps, learn about: - Particles and Effects - Add visual effects - Cameras and Scrolling - Combine with scrolling - Performance Optimization - Optimize rendering

    See also: - API Reference - TileMap - API Reference - Renderer - Manual - Cameras and Scrolling

    "},{"location":"manual/game_development/audio/","title":"Audio","text":"

    PixelRoot32 includes a complete NES-like audio system with 4 channels for sound effects and background music. This guide shows you how to add sound and music to your games.

    "},{"location":"manual/game_development/audio/#audio-configuration","title":"Audio Configuration","text":"

    Before using audio, you need to configure an AudioBackend. This is done when creating the Engine:

    "},{"location":"manual/game_development/audio/#esp32-internal-dac","title":"ESP32: Internal DAC","text":"
    #include <drivers/esp32/ESP32_DAC_AudioBackend.h>\n\nconst int DAC_PIN = 25; // GPIO 25 or 26\npr32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(DAC_PIN, 11025);\n\npr32::audio::AudioConfig audioConfig(&audioBackend, audioBackend.getSampleRate());\n
    "},{"location":"manual/game_development/audio/#esp32-external-i2s-dac","title":"ESP32: External I2S DAC","text":"
    #include <drivers/esp32/ESP32_I2S_AudioBackend.h>\n\nconst int I2S_BCLK = 26;  // Bit clock\nconst int I2S_LRCK = 25;  // Left/Right clock\nconst int I2S_DOUT = 22;  // Data out\n\npr32::drivers::esp32::ESP32_I2S_AudioBackend audioBackend(\n    I2S_BCLK, I2S_LRCK, I2S_DOUT, 22050\n);\n\npr32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n
    "},{"location":"manual/game_development/audio/#native-pc-sdl2","title":"Native (PC): SDL2","text":"
    #include <drivers/native/SDL2_AudioBackend.h>\n\npr32::drivers::native::SDL2_AudioBackend audioBackend(22050, 1024);\n\npr32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n
    "},{"location":"manual/game_development/audio/#sound-effects","title":"Sound Effects","text":"

    Sound effects are created using AudioEvent structures and played through the AudioEngine.

    "},{"location":"manual/game_development/audio/#audioevent-structure","title":"AudioEvent Structure","text":"
    #include <audio/AudioTypes.h>\n\npr32::audio::AudioEvent soundEffect{};\nsoundEffect.type = pr32::audio::WaveType::PULSE;  // Waveform type\nsoundEffect.frequency = 1500.0f;                  // Frequency in Hz\nsoundEffect.duration = 0.12f;                      // Duration in seconds\nsoundEffect.volume = 0.8f;                         // Volume (0.0 to 1.0)\nsoundEffect.duty = 0.5f;                           // Duty cycle (for PULSE only)\n
    "},{"location":"manual/game_development/audio/#wave-types","title":"Wave Types","text":"

    PixelRoot32 supports three wave types:

    • PULSE: Square wave with variable duty cycle
    • Duty cycles: 0.125 (thin), 0.25 (classic NES), 0.5 (symmetric), 0.75 (fat)
    • Good for: Beeps, jumps, UI sounds, leads

    • TRIANGLE: Triangle wave (fixed volume/duty)

    • Softer, smoother sound
    • Good for: Bass lines, pads, background tones

    • NOISE: Pseudo-random noise

    • Harsh, chaotic sound
    • Good for: Explosions, hits, impacts, drums
    "},{"location":"manual/game_development/audio/#playing-sound-effects","title":"Playing Sound Effects","text":"
    // Get the audio engine\nauto& audio = engine.getAudioEngine();\n\n// Create and play a sound\npr32::audio::AudioEvent jumpSound{};\njumpSound.type = pr32::audio::WaveType::PULSE;\njumpSound.frequency = 800.0f;\njumpSound.duration = 0.1f;\njumpSound.volume = 0.7f;\njumpSound.duty = 0.25f;\n\naudio.playEvent(jumpSound);\n
    "},{"location":"manual/game_development/audio/#common-sound-effects","title":"Common Sound Effects","text":"

    Here are some example sound effects you can use:

    namespace SoundEffects {\n    // Jump sound\n    inline pr32::audio::AudioEvent jump() {\n        pr32::audio::AudioEvent evt{};\n        evt.type = pr32::audio::WaveType::PULSE;\n        evt.frequency = 600.0f;\n        evt.duration = 0.1f;\n        evt.volume = 0.7f;\n        evt.duty = 0.25f;\n        return evt;\n    }\n\n    // Coin/collect sound\n    inline pr32::audio::AudioEvent coin() {\n        pr32::audio::AudioEvent evt{};\n        evt.type = pr32::audio::WaveType::PULSE;\n        evt.frequency = 1500.0f;\n        evt.duration = 0.12f;\n        evt.volume = 0.8f;\n        evt.duty = 0.5f;\n        return evt;\n    }\n\n    // Explosion\n    inline pr32::audio::AudioEvent explosion() {\n        pr32::audio::AudioEvent evt{};\n        evt.type = pr32::audio::WaveType::NOISE;\n        evt.frequency = 200.0f;\n        evt.duration = 0.3f;\n        evt.volume = 0.9f;\n        return evt;\n    }\n\n    // Hit/damage\n    inline pr32::audio::AudioEvent hit() {\n        pr32::audio::AudioEvent evt{};\n        evt.type = pr32::audio::WaveType::NOISE;\n        evt.frequency = 300.0f;\n        evt.duration = 0.15f;\n        evt.volume = 0.6f;\n        return evt;\n    }\n}\n\n// Usage\naudio.playEvent(SoundEffects::jump());\n
    "},{"location":"manual/game_development/audio/#background-music","title":"Background Music","text":"

    Background music uses the MusicPlayer system, which sequences notes over time.

    "},{"location":"manual/game_development/audio/#music-notes","title":"Music Notes","text":"

    Music is built from MusicNote structures:

    #include <audio/AudioMusicTypes.h>\n\nusing namespace pr32::audio;\n\nMusicNote note{};\nnote.note = Note::C;        // Musical note (C, D, E, F, G, A, B, or Rest)\nnote.octave = 4;            // Octave (0-8)\nnote.duration = 0.2f;       // Duration in seconds\nnote.volume = 0.7f;         // Volume (0.0 to 1.0)\n
    "},{"location":"manual/game_development/audio/#instrument-presets","title":"Instrument Presets","text":"

    For convenience, use predefined instrument presets:

    using namespace pr32::audio;\n\n// Available presets:\n// - INSTR_PULSE_LEAD: Main lead pulse (octave 4)\n// - INSTR_PULSE_BASS: Bass pulse (octave 3)\n// - INSTR_PULSE_CHIP_HIGH: High-pitched chiptune (octave 5)\n// - INSTR_TRIANGLE_PAD: Soft triangle pad (octave 4)\n
    "},{"location":"manual/game_development/audio/#creating-a-melody","title":"Creating a Melody","text":"
    #include <audio/AudioMusicTypes.h>\n\nusing namespace pr32::audio;\n\n// Define melody notes\nstatic const MusicNote MELODY_NOTES[] = {\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::E, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::G, 0.25f),\n    makeRest(0.10f),  // Rest (silence)\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::E, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::G, 0.25f),\n    makeRest(0.10f),\n};\n\n// Create music track\nstatic const MusicTrack GAME_MUSIC = {\n    MELODY_NOTES,                              // notes array\n    sizeof(MELODY_NOTES) / sizeof(MusicNote),  // note count\n    true,                                       // loop\n    WaveType::PULSE,                           // channel type\n    0.5f                                       // duty cycle\n};\n
    "},{"location":"manual/game_development/audio/#playing-music","title":"Playing Music","text":"
    // Get the music player\nauto& music = engine.getMusicPlayer();\n\n// Play a track\nmusic.play(GAME_MUSIC);\n\n// Control playback\nmusic.stop();   // Stop playback\nmusic.pause();  // Pause (time doesn't advance)\nmusic.resume(); // Resume after pause\n\n// Check status\nif (music.isPlaying()) {\n    // Music is currently playing\n}\n
    "},{"location":"manual/game_development/audio/#music-in-scene","title":"Music in Scene","text":"

    Typically, you start music in your scene's init():

    void MyGameScene::init() override {\n    // Start background music\n    engine.getMusicPlayer().play(GAME_MUSIC);\n\n    // ... rest of initialization\n}\n
    "},{"location":"manual/game_development/audio/#master-volume","title":"Master Volume","text":"

    Control overall volume without changing individual sounds:

    auto& audio = engine.getAudioEngine();\n\n// Set master volume (0.0 to 1.0)\naudio.setMasterVolume(0.5f); // 50% volume\n\n// Get current volume\nfloat currentVolume = audio.getMasterVolume();\n
    "},{"location":"manual/game_development/audio/#complete-example","title":"Complete Example","text":"

    Here's a complete example combining sound effects and music:

    #include <core/Scene.h>\n#include <audio/AudioTypes.h>\n#include <audio/AudioMusicTypes.h>\n\nusing namespace pr32::audio;\n\n// Background music\nstatic const MusicNote GAME_MELODY[] = {\n    makeNote(INSTR_PULSE_LEAD, Note::C, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::E, 0.20f),\n    makeNote(INSTR_PULSE_LEAD, Note::G, 0.25f),\n    makeRest(0.10f),\n};\n\nstatic const MusicTrack BACKGROUND_MUSIC = {\n    GAME_MELODY,\n    sizeof(GAME_MELODY) / sizeof(MusicNote),\n    true,  // loop\n    WaveType::PULSE,\n    0.5f\n};\n\nclass AudioExampleScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Start background music\n        engine.getMusicPlayer().play(BACKGROUND_MUSIC);\n\n        // Set master volume\n        engine.getAudioEngine().setMasterVolume(0.8f);\n    }\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        auto& audio = engine.getAudioEngine();\n\n        // Play sound effect on button press\n        if (input.isButtonPressed(4)) { // Button A\n            AudioEvent jumpSound{};\n            jumpSound.type = WaveType::PULSE;\n            jumpSound.frequency = 800.0f;\n            jumpSound.duration = 0.1f;\n            jumpSound.volume = 0.7f;\n            jumpSound.duty = 0.25f;\n\n            audio.playEvent(jumpSound);\n        }\n\n        Scene::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/game_development/audio/#designing-nes-like-sounds","title":"Designing NES-like Sounds","text":""},{"location":"manual/game_development/audio/#frequency-guidelines","title":"Frequency Guidelines","text":"
    • Low frequencies (200-400 Hz): Bass, impacts, explosions
    • Mid frequencies (400-1000 Hz): Main sounds, jumps, UI
    • High frequencies (1000-2000 Hz): Beeps, coins, pickups
    • Very high (2000+ Hz): Sharp sounds, alerts
    "},{"location":"manual/game_development/audio/#duration-guidelines","title":"Duration Guidelines","text":"
    • Short (0.05-0.1s): UI clicks, small effects
    • Medium (0.1-0.2s): Jumps, hits, pickups
    • Long (0.2-0.5s): Explosions, power-ups, transitions
    "},{"location":"manual/game_development/audio/#duty-cycle-pulse-only","title":"Duty Cycle (PULSE only)","text":"
    • 0.125: Thin, sharp, piercing
    • 0.25: Classic NES lead sound
    • 0.5: Symmetric, full, fat
    • 0.75: Very fat, bass-like
    "},{"location":"manual/game_development/audio/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/audio/#sound-design","title":"Sound Design","text":"
    • Keep sounds short: Long sounds can overlap and cause issues
    • Use appropriate volumes: 0.6-0.8 is usually good for effects
    • Vary frequencies: Don't use the same frequency for everything
    • Test on hardware: ESP32 audio may sound different than PC
    "},{"location":"manual/game_development/audio/#music","title":"Music","text":"
    • Use one channel for music: Leave other channels for SFX
    • Keep melodies simple: Complex melodies can be hard to follow
    • Loop seamlessly: End your melody where it can loop naturally
    • Consider tempo: Faster games need faster music
    "},{"location":"manual/game_development/audio/#performance","title":"Performance","text":"
    • Limit simultaneous sounds: Only 4 channels total
    • Music uses one channel: Plan your SFX accordingly
    • Don't spam sounds: Too many sounds can cause audio glitches
    • Use master volume: Easier than adjusting individual sounds
    "},{"location":"manual/game_development/audio/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/audio/#sound-effect-helper-function","title":"Sound Effect Helper Function","text":"
    void playJumpSound() {\n    auto& audio = engine.getAudioEngine();\n    AudioEvent evt{};\n    evt.type = WaveType::PULSE;\n    evt.frequency = 600.0f;\n    evt.duration = 0.1f;\n    evt.volume = 0.7f;\n    evt.duty = 0.25f;\n    audio.playEvent(evt);\n}\n
    "},{"location":"manual/game_development/audio/#music-state-management","title":"Music State Management","text":"
    class GameScene : public Scene {\n    bool musicStarted = false;\n\n    void init() override {\n        // Don't start music here if scene can be re-initialized\n    }\n\n    void update(unsigned long deltaTime) override {\n        if (!musicStarted) {\n            engine.getMusicPlayer().play(GAME_MUSIC);\n            musicStarted = true;\n        }\n        Scene::update(deltaTime);\n    }\n};\n
    "},{"location":"manual/game_development/audio/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/game_development/audio/#no-sound","title":"No Sound","text":"
    • Check audio backend is configured correctly
    • Verify sample rate matches backend
    • Check master volume is not 0
    • Ensure audio is initialized (engine.init())
    "},{"location":"manual/game_development/audio/#distorted-sound","title":"Distorted Sound","text":"
    • Lower volume levels
    • Reduce sample rate (ESP32 DAC works better at 11025 Hz)
    • Check for too many simultaneous sounds
    • Verify hardware connections (ESP32)
    "},{"location":"manual/game_development/audio/#music-not-playing","title":"Music Not Playing","text":"
    • Check music.isPlaying() status
    • Ensure track is properly defined
    • Verify MusicPlayer is updated (happens automatically)
    • Check that music channel is not being used by SFX
    "},{"location":"manual/game_development/audio/#next-steps","title":"Next Steps","text":"

    Now that you can add audio, learn about: - NES Audio Reference - Advanced audio techniques - Physics and Collisions - Make objects interact - User Interface - Create menus and HUDs

    See also: - API Reference - AudioEngine - API Reference - MusicPlayer - API Reference - Audio Types - Manual - Audio Overview

    "},{"location":"manual/game_development/basic_rendering/","title":"Basic Rendering","text":"

    Rendering is how you draw everything on screen. This guide covers the fundamental drawing operations in PixelRoot32: primitives, sprites, and text.

    "},{"location":"manual/game_development/basic_rendering/#accessing-the-renderer","title":"Accessing the Renderer","text":"

    You can access the renderer in two ways:

    "},{"location":"manual/game_development/basic_rendering/#from-the-engine","title":"From the Engine","text":"
    auto& renderer = engine.getRenderer();\n
    "},{"location":"manual/game_development/basic_rendering/#from-a-scene","title":"From a Scene","text":"
    void MyScene::draw(pixelroot32::graphics::Renderer& renderer) override {\n    // renderer is passed as parameter\n    renderer.drawFilledRectangle(0, 0, 240, 240, Color::Black);\n}\n
    "},{"location":"manual/game_development/basic_rendering/#drawing-primitives","title":"Drawing Primitives","text":""},{"location":"manual/game_development/basic_rendering/#pixels","title":"Pixels","text":"

    Draw a single pixel:

    renderer.drawPixel(100, 100, pixelroot32::graphics::Color::White);\n
    "},{"location":"manual/game_development/basic_rendering/#lines","title":"Lines","text":"

    Draw a line between two points:

    renderer.drawLine(10, 10, 200, 200, pixelroot32::graphics::Color::Red);\n
    "},{"location":"manual/game_development/basic_rendering/#rectangles","title":"Rectangles","text":"

    Draw a rectangle outline:

    renderer.drawRectangle(50, 50, 100, 80, pixelroot32::graphics::Color::Blue);\n

    Draw a filled rectangle:

    renderer.drawFilledRectangle(50, 50, 100, 80, pixelroot32::graphics::Color::Blue);\n
    "},{"location":"manual/game_development/basic_rendering/#circles","title":"Circles","text":"

    Draw a circle outline:

    renderer.drawCircle(120, 120, 30, pixelroot32::graphics::Color::Green);\n

    Draw a filled circle:

    renderer.drawFilledCircle(120, 120, 30, pixelroot32::graphics::Color::Green);\n
    "},{"location":"manual/game_development/basic_rendering/#simple-sprites-1bpp","title":"Simple Sprites (1bpp)","text":"

    Sprites are the primary way to draw game graphics. The standard format is 1bpp (1 bit per pixel), which is memory-efficient and perfect for retro-style graphics.

    "},{"location":"manual/game_development/basic_rendering/#creating-a-sprite-manually","title":"Creating a Sprite Manually","text":"

    A 1bpp sprite is defined as an array of uint16_t, where each value represents one row:

    #include <graphics/Renderer.h>\n\n// Example: 8x8 sprite (8 rows, each row is a uint16_t)\n// Bit 0 = leftmost pixel, bit 7 = rightmost pixel\nstatic const uint16_t MY_SPRITE_DATA[] = {\n    0b00111100,  // Row 0:   ..####..\n    0b01111110,  // Row 1:  .######.\n    0b11111111,  // Row 2:  ########\n    0b11111111,  // Row 3:  ########\n    0b11111111,  // Row 4:  ########\n    0b01111110,  // Row 5:  .######.\n    0b00111100,  // Row 6:   ..####..\n    0b00000000   // Row 7:  ........\n};\n\n// Create sprite descriptor\nstatic const pixelroot32::graphics::Sprite MY_SPRITE = {\n    MY_SPRITE_DATA,  // data pointer\n    8,                // width\n    8                 // height\n};\n
    "},{"location":"manual/game_development/basic_rendering/#drawing-a-sprite","title":"Drawing a Sprite","text":"
    renderer.drawSprite(\n    MY_SPRITE,                                    // sprite\n    100,                                          // x position\n    100,                                          // y position\n    pixelroot32::graphics::Color::White,         // color\n    false                                         // flipX (optional, default false)\n);\n
    "},{"location":"manual/game_development/basic_rendering/#sprite-bit-convention","title":"Sprite Bit Convention","text":"
    • Each uint16_t represents one row
    • Bit 0 (rightmost bit) = leftmost pixel
    • Bit (width - 1) = rightmost pixel
    • 0 = transparent/off, 1 = on (colored)

    Example: For an 8-pixel wide sprite:

    Row value: 0b00111100\nPixels:    ..####..\n           ^      ^\n         bit 7  bit 0 (leftmost)\n

    "},{"location":"manual/game_development/basic_rendering/#flipping-sprites","title":"Flipping Sprites","text":"

    You can flip a sprite horizontally:

    renderer.drawSprite(MY_SPRITE, 100, 100, Color::White, true); // flipped\n
    "},{"location":"manual/game_development/basic_rendering/#text-rendering","title":"Text Rendering","text":"

    PixelRoot32 uses a native bitmap font system for pixel-perfect text rendering.

    "},{"location":"manual/game_development/basic_rendering/#drawing-text","title":"Drawing Text","text":"
    renderer.drawText(\n    \"Hello World!\",                           // text string\n    10,                                       // x position\n    20,                                       // y position\n    pixelroot32::graphics::Color::White,     // color\n    1                                         // size multiplier (1=normal, 2=double, etc.)\n);\n
    "},{"location":"manual/game_development/basic_rendering/#centered-text","title":"Centered Text","text":"
    renderer.drawTextCentered(\n    \"Game Over\",                              // text\n    120,                                      // y position (centered horizontally)\n    pixelroot32::graphics::Color::Red,       // color\n    2                                         // size\n);\n
    "},{"location":"manual/game_development/basic_rendering/#text-size","title":"Text Size","text":"

    The size parameter multiplies the font size: - 1 = normal size (5x7 pixels per character with default font) - 2 = double size (10x14 pixels) - 3 = triple size (15x21 pixels) - etc.

    "},{"location":"manual/game_development/basic_rendering/#supported-characters","title":"Supported Characters","text":"

    The default font (FONT_5X7) supports ASCII characters 32-126: - Letters: A-Z, a-z - Numbers: 0-9 - Symbols: !@#$%^&*()_+-=[]{}|;:'\",.<>?/ etc.

    "},{"location":"manual/game_development/basic_rendering/#render-layers","title":"Render Layers","text":"

    Entities are drawn in order based on their renderLayer property:

    • Layer 0 (Background): Drawn first (behind everything)
    • Layer 1 (Gameplay): Drawn second (main game objects)
    • Layer 2 (UI): Drawn last (on top of everything)

    Set the render layer when creating an entity:

    myEntity->setRenderLayer(0); // Background\nmyEntity->setRenderLayer(1); // Gameplay (default)\nmyEntity->setRenderLayer(2); // UI\n
    "},{"location":"manual/game_development/basic_rendering/#complete-example","title":"Complete Example","text":"

    Here's a complete example that draws various primitives and a sprite:

    #include <core/Scene.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\n// Simple sprite data (8x8 circle)\nstatic const uint16_t CIRCLE_SPRITE_DATA[] = {\n    0b00111100,\n    0b01111110,\n    0b11111111,\n    0b11111111,\n    0b11111111,\n    0b11111111,\n    0b01111110,\n    0b00111100\n};\n\nstatic const pixelroot32::graphics::Sprite CIRCLE_SPRITE = {\n    CIRCLE_SPRITE_DATA,\n    8,\n    8\n};\n\nclass RenderingExampleScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Set palette\n        pixelroot32::graphics::setPalette(\n            pixelroot32::graphics::PaletteType::NES\n        );\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw background (layer 0)\n        renderer.drawFilledRectangle(0, 0, 240, 240, \n            pixelroot32::graphics::Color::Navy);\n\n        // Draw primitives (layer 1)\n        renderer.drawRectangle(10, 10, 100, 80, \n            pixelroot32::graphics::Color::White);\n        renderer.drawFilledCircle(120, 120, 30, \n            pixelroot32::graphics::Color::Red);\n        renderer.drawLine(0, 0, 240, 240, \n            pixelroot32::graphics::Color::Yellow);\n\n        // Draw sprite\n        renderer.drawSprite(CIRCLE_SPRITE, 50, 50, \n            pixelroot32::graphics::Color::Cyan);\n\n        // Draw text (layer 2 - UI)\n        renderer.drawText(\"Score: 100\", 10, 10, \n            pixelroot32::graphics::Color::White, 1);\n        renderer.drawTextCentered(\"Rendering Demo\", 200, \n            pixelroot32::graphics::Color::Yellow, 2);\n\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/game_development/basic_rendering/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/basic_rendering/#performance","title":"Performance","text":"
    • Minimize draw calls: Batch similar operations when possible
    • Use render layers efficiently: Don't mix layers unnecessarily
    • Avoid drawing off-screen: Check bounds before drawing
    • Reuse sprites: Define sprites once, use many times
    "},{"location":"manual/game_development/basic_rendering/#organization","title":"Organization","text":"
    • Define sprites as static const: Keep them in flash memory
    • Use meaningful names: Name your sprites clearly
    • Group related sprites: Organize sprite data logically
    • Document complex sprites: Comment sprite bit patterns if needed
    "},{"location":"manual/game_development/basic_rendering/#text","title":"Text","text":"
    • Avoid frequent text updates: Text rendering has overhead
    • Use appropriate sizes: Larger text uses more memory
    • Cache text dimensions: Use FontManager::textWidth() if needed
    • Keep text simple: Complex formatting is not supported
    "},{"location":"manual/game_development/basic_rendering/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/basic_rendering/#drawing-a-background","title":"Drawing a Background","text":"
    void drawBackground(Renderer& renderer) {\n    // Solid color background\n    renderer.drawFilledRectangle(0, 0, 240, 240, Color::Black);\n\n    // Or use a tilemap (see Advanced Graphics section)\n}\n
    "},{"location":"manual/game_development/basic_rendering/#drawing-a-hud","title":"Drawing a HUD","text":"
    void drawHUD(Renderer& renderer, int score, int lives) {\n    char buffer[32];\n    snprintf(buffer, sizeof(buffer), \"Score: %d\", score);\n    renderer.drawText(buffer, 10, 10, Color::White, 1);\n\n    snprintf(buffer, sizeof(buffer), \"Lives: %d\", lives);\n    renderer.drawText(buffer, 10, 20, Color::White, 1);\n}\n
    "},{"location":"manual/game_development/basic_rendering/#drawing-multiple-sprites","title":"Drawing Multiple Sprites","text":"
    void drawSpriteArray(Renderer& renderer, const Sprite& sprite, \n                     int count, int startX, int startY, int spacing) {\n    for (int i = 0; i < count; i++) {\n        int x = startX + (i * (sprite.width + spacing));\n        renderer.drawSprite(sprite, x, startY, Color::White);\n    }\n}\n
    "},{"location":"manual/game_development/basic_rendering/#next-steps","title":"Next Steps","text":"

    Now that you can draw basic graphics, learn about: - Sprites and Animation - Advanced sprite techniques - Input and Control - Make your game interactive - Palettes - Use different color schemes

    See also: - API Reference - Renderer - API Reference - Sprite - Render Layers

    "},{"location":"manual/game_development/input_and_control/","title":"Input and Control","text":"

    Handling user input is essential for any interactive game. This guide covers how to read and process input from buttons (ESP32) or keyboard (Native) in PixelRoot32.

    "},{"location":"manual/game_development/input_and_control/#input-configuration","title":"Input Configuration","text":"

    Before you can read input, you need to configure the InputManager. This is done when creating the Engine:

    "},{"location":"manual/game_development/input_and_control/#esp32-configuration","title":"ESP32 Configuration","text":"
    #include <input/InputConfig.h>\n\n// InputConfig(buttonCount, UP, DOWN, LEFT, RIGHT, A, B)\npr32::input::InputConfig inputConfig(\n    6,      // Total number of buttons\n    32,     // UP button GPIO pin\n    27,     // DOWN button GPIO pin\n    33,     // LEFT button GPIO pin\n    14,     // RIGHT button GPIO pin\n    13,     // A button GPIO pin\n    12      // B button GPIO pin\n);\n
    "},{"location":"manual/game_development/input_and_control/#native-pc-configuration","title":"Native (PC) Configuration","text":"
    #include <SDL2/SDL.h>\n#include <input/InputConfig.h>\n\n// InputConfig(buttonCount, UP, DOWN, LEFT, RIGHT, A, B)\npr32::input::InputConfig inputConfig(\n    6,                      // Total number of buttons\n    SDL_SCANCODE_UP,        // UP key\n    SDL_SCANCODE_DOWN,      // DOWN key\n    SDL_SCANCODE_LEFT,      // LEFT key\n    SDL_SCANCODE_RIGHT,     // RIGHT key\n    SDL_SCANCODE_SPACE,     // A button (Space)\n    SDL_SCANCODE_RETURN     // B button (Enter)\n);\n
    "},{"location":"manual/game_development/input_and_control/#reading-input","title":"Reading Input","text":"

    Access the InputManager through the Engine:

    auto& input = engine.getInputManager();\n
    "},{"location":"manual/game_development/input_and_control/#input-states","title":"Input States","text":"

    The InputManager provides four different ways to check button state:

    "},{"location":"manual/game_development/input_and_control/#1-isbuttonpressed","title":"1. isButtonPressed()","text":"

    Returns true only on the frame when the button was just pressed:

    if (input.isButtonPressed(4)) { // Button A (index 4)\n    // This code runs only once when button is first pressed\n    jump();\n}\n

    Use for: Actions that should trigger once per press (jump, shoot, select menu item).

    "},{"location":"manual/game_development/input_and_control/#2-isbuttonreleased","title":"2. isButtonReleased()","text":"

    Returns true only on the frame when the button was just released:

    if (input.isButtonReleased(4)) {\n    // This code runs only once when button is released\n    stopCharging();\n}\n

    Use for: Actions that trigger on release (charge attacks, menu confirmation).

    "},{"location":"manual/game_development/input_and_control/#3-isbuttondown","title":"3. isButtonDown()","text":"

    Returns true while the button is currently held down:

    if (input.isButtonDown(2)) { // LEFT button (index 2)\n    // This code runs every frame while button is held\n    playerX -= speed * (deltaTime * 0.001f);\n}\n

    Use for: Continuous actions (movement, holding, charging).

    "},{"location":"manual/game_development/input_and_control/#4-isbuttonclicked","title":"4. isButtonClicked()","text":"

    Returns true when the button was pressed and then released:

    if (input.isButtonClicked(4)) {\n    // This code runs once per click (press + release cycle)\n    toggleMenu();\n}\n

    Use for: Toggle actions, menu selections, click interactions.

    "},{"location":"manual/game_development/input_and_control/#button-indices","title":"Button Indices","text":"

    The button indices correspond to the order in InputConfig:

    • Index 0: UP
    • Index 1: DOWN
    • Index 2: LEFT
    • Index 3: RIGHT
    • Index 4: A button
    • Index 5: B button

    For convenience, you can define constants:

    namespace Buttons {\n    constexpr uint8_t UP = 0;\n    constexpr uint8_t DOWN = 1;\n    constexpr uint8_t LEFT = 2;\n    constexpr uint8_t RIGHT = 3;\n    constexpr uint8_t A = 4;\n    constexpr uint8_t B = 5;\n}\n\n// Usage\nif (input.isButtonPressed(Buttons::A)) {\n    // ...\n}\n
    "},{"location":"manual/game_development/input_and_control/#character-control-example","title":"Character Control Example","text":"

    Here's a complete example of character movement:

    #include <core/Actor.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\nclass PlayerActor : public pixelroot32::core::Actor {\npublic:\n    float speed = 100.0f; // pixels per second\n\n    PlayerActor(float x, float y)\n        : Actor(x, y, 16, 16) {\n        setRenderLayer(1);\n    }\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        float dt = deltaTime * 0.001f; // Convert to seconds\n\n        // Horizontal movement\n        if (input.isButtonDown(Buttons::LEFT)) {\n            x -= speed * dt;\n        }\n        if (input.isButtonDown(Buttons::RIGHT)) {\n            x += speed * dt;\n        }\n\n        // Vertical movement\n        if (input.isButtonDown(Buttons::UP)) {\n            y -= speed * dt;\n        }\n        if (input.isButtonDown(Buttons::DOWN)) {\n            y += speed * dt;\n        }\n\n        // Keep player on screen\n        if (x < 0) x = 0;\n        if (x > 224) x = 224;\n        if (y < 0) y = 0;\n        if (y > 224) y = 224;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x),\n            static_cast<int>(y),\n            width,\n            height,\n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Handle collisions\n    }\n};\n
    "},{"location":"manual/game_development/input_and_control/#jumping-example","title":"Jumping Example","text":"

    For platformer-style jumping:

    class PlatformerPlayer : public pixelroot32::core::PhysicsActor {\npublic:\n    bool canJump = true;\n    float jumpForce = 200.0f;\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        float dt = deltaTime * 0.001f;\n\n        // Horizontal movement\n        float moveSpeed = 80.0f;\n        if (input.isButtonDown(Buttons::LEFT)) {\n            setVelocity(-moveSpeed, vy);\n        } else if (input.isButtonDown(Buttons::RIGHT)) {\n            setVelocity(moveSpeed, vy);\n        } else {\n            setVelocity(0, vy);\n        }\n\n        // Jump (only on press, and only if on ground)\n        if (input.isButtonPressed(Buttons::A) && canJump) {\n            setVelocity(vx, -jumpForce);\n            canJump = false;\n        }\n\n        // Check if on ground (for jump reset)\n        auto collisionInfo = getWorldCollisionInfo();\n        if (collisionInfo.bottom) {\n            canJump = true;\n        }\n\n        PhysicsActor::update(deltaTime);\n    }\n};\n
    "},{"location":"manual/game_development/input_and_control/#shooting-example","title":"Shooting Example","text":"

    For shooting projectiles:

    class ShooterActor : public pixelroot32::core::Actor {\nprivate:\n    bool fireInputReady = true;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n\n        // Shooting (with cooldown)\n        if (input.isButtonPressed(Buttons::A) && fireInputReady) {\n            shoot();\n            fireInputReady = false;\n        }\n\n        // Reset fire input when button is released\n        if (input.isButtonReleased(Buttons::A)) {\n            fireInputReady = true;\n        }\n    }\n\n    void shoot() {\n        // Create projectile\n        // ...\n    }\n};\n
    "},{"location":"manual/game_development/input_and_control/#menu-navigation-example","title":"Menu Navigation Example","text":"

    For menu navigation:

    class MenuScene : public pixelroot32::core::Scene {\nprivate:\n    int selectedIndex = 0;\n    static const int MENU_ITEMS = 3;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n\n        // Navigate menu\n        if (input.isButtonPressed(Buttons::UP)) {\n            selectedIndex--;\n            if (selectedIndex < 0) selectedIndex = MENU_ITEMS - 1;\n        }\n\n        if (input.isButtonPressed(Buttons::DOWN)) {\n            selectedIndex++;\n            if (selectedIndex >= MENU_ITEMS) selectedIndex = 0;\n        }\n\n        // Select menu item\n        if (input.isButtonPressed(Buttons::A)) {\n            selectMenuItem(selectedIndex);\n        }\n\n        Scene::update(deltaTime);\n    }\n};\n
    "},{"location":"manual/game_development/input_and_control/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/input_and_control/#frame-rate-independence","title":"Frame-Rate Independence","text":"

    Always multiply movement by delta time:

    // \u2705 GOOD: Framerate-independent\nx += speed * (deltaTime * 0.001f);\n\n// \u274c BAD: Framerate-dependent\nx += speed;\n
    "},{"location":"manual/game_development/input_and_control/#input-debouncing","title":"Input Debouncing","text":"

    For actions that should only trigger once:

    // Use isButtonPressed() instead of isButtonDown()\nif (input.isButtonPressed(Buttons::A)) {\n    // Triggers once per press\n}\n
    "},{"location":"manual/game_development/input_and_control/#input-buffering","title":"Input Buffering","text":"

    For responsive controls, you can buffer input:

    class InputBuffer {\n    uint8_t bufferedInput = 0;\n\npublic:\n    void update(const InputManager& input) {\n        if (input.isButtonPressed(Buttons::A)) {\n            bufferedInput = Buttons::A;\n        }\n    }\n\n    bool hasBufferedInput() const { return bufferedInput != 0; }\n    uint8_t consumeInput() {\n        uint8_t result = bufferedInput;\n        bufferedInput = 0;\n        return result;\n    }\n};\n
    "},{"location":"manual/game_development/input_and_control/#multiple-input-methods","title":"Multiple Input Methods","text":"

    Support both button presses and held buttons:

    // Allow both tap and hold for rapid fire\nif (input.isButtonPressed(Buttons::A) || \n    (input.isButtonDown(Buttons::A) && rapidFireTimer <= 0)) {\n    shoot();\n    rapidFireTimer = RAPID_FIRE_DELAY;\n}\n
    "},{"location":"manual/game_development/input_and_control/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/input_and_control/#directional-input","title":"Directional Input","text":"
    float getHorizontalInput() {\n    auto& input = engine.getInputManager();\n    float dir = 0.0f;\n    if (input.isButtonDown(Buttons::LEFT)) dir -= 1.0f;\n    if (input.isButtonDown(Buttons::RIGHT)) dir += 1.0f;\n    return dir;\n}\n\nfloat getVerticalInput() {\n    auto& input = engine.getInputManager();\n    float dir = 0.0f;\n    if (input.isButtonDown(Buttons::UP)) dir -= 1.0f;\n    if (input.isButtonDown(Buttons::DOWN)) dir += 1.0f;\n    return dir;\n}\n
    "},{"location":"manual/game_development/input_and_control/#input-state-machine","title":"Input State Machine","text":"

    For complex input handling:

    enum class InputState {\n    IDLE,\n    PRESSED,\n    HELD,\n    RELEASED\n};\n\nInputState getButtonState(const InputManager& input, uint8_t button) {\n    if (input.isButtonPressed(button)) return InputState::PRESSED;\n    if (input.isButtonDown(button)) return InputState::HELD;\n    if (input.isButtonReleased(button)) return InputState::RELEASED;\n    return InputState::IDLE;\n}\n
    "},{"location":"manual/game_development/input_and_control/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/game_development/input_and_control/#button-not-responding","title":"Button Not Responding","text":"
    • Check button indices match your InputConfig
    • Verify GPIO pins (ESP32) or scancodes (Native) are correct
    • Ensure InputManager is being updated (happens automatically in Engine)
    "},{"location":"manual/game_development/input_and_control/#input-feels-laggy","title":"Input Feels Laggy","text":"
    • Ensure you're using deltaTime for movement
    • Check that input is read in update(), not draw()
    • Verify framerate is stable
    "},{"location":"manual/game_development/input_and_control/#multiple-triggers","title":"Multiple Triggers","text":"
    • Use isButtonPressed() instead of isButtonDown() for one-time actions
    • Implement input buffering or cooldown timers
    "},{"location":"manual/game_development/input_and_control/#next-steps","title":"Next Steps","text":"

    Now that you can handle input, learn about: - Audio - Add sound effects and music - Physics and Collisions - Make objects interact - User Interface - Create menus and HUDs

    See also: - API Reference - InputManager - API Reference - InputConfig - Manual - Input Overview

    "},{"location":"manual/game_development/physics_and_collisions/","title":"Physics and Collisions","text":"

    PixelRoot32 provides a physics system for moving objects and collision detection. This guide covers PhysicsActor for automatic physics and the collision system for detecting interactions between objects.

    "},{"location":"manual/game_development/physics_and_collisions/#physicsactor","title":"PhysicsActor","text":"

    A PhysicsActor is an Actor that automatically handles physics: velocity, gravity, friction, and world boundary collisions.

    "},{"location":"manual/game_development/physics_and_collisions/#creating-a-physicsactor","title":"Creating a PhysicsActor","text":"
    #include <core/PhysicsActor.h>\n\nclass Ball : public pixelroot32::core::PhysicsActor {\npublic:\n    Ball(float x, float y, float radius)\n        : PhysicsActor(x, y, radius * 2, radius * 2) {\n        setRenderLayer(1);\n\n        // Set physics properties\n        setRestitution(0.8f);  // Bounciness (0.8 = 80% bounce)\n        setFriction(0.1f);     // Friction (0.1 = slight friction)\n\n        // Set world boundaries\n        setWorldSize(240, 240);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Apply gravity\n        // (PhysicsActor handles this automatically, but you can add custom forces)\n\n        // Call parent update to apply physics\n        PhysicsActor::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        int radius = width / 2;\n        renderer.drawFilledCircle(\n            static_cast<int>(x + radius),\n            static_cast<int>(y + radius),\n            radius,\n            pixelroot32::graphics::Color::White\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Handle collision with other actors\n    }\n\n    void onWorldCollision() override {\n        // Called when hitting world boundaries\n        // You can play a sound effect here, for example\n    }\n};\n
    "},{"location":"manual/game_development/physics_and_collisions/#physics-properties","title":"Physics Properties","text":""},{"location":"manual/game_development/physics_and_collisions/#velocity","title":"Velocity","text":"

    Set the velocity directly:

    ball->setVelocity(100.0f, -50.0f); // Move right at 100 px/s, up at 50 px/s\n

    Or modify existing velocity:

    float vx = ball->vx; // Access velocity components (protected, use getter if needed)\nball->setVelocity(vx + 10.0f, ball->vy); // Accelerate horizontally\n
    "},{"location":"manual/game_development/physics_and_collisions/#restitution-bounciness","title":"Restitution (Bounciness)","text":"

    Controls how much energy is conserved in collisions:

    ball->setRestitution(1.0f);  // Perfect bounce (no energy loss)\nball->setRestitution(0.5f);  // 50% energy loss\nball->setRestitution(0.0f);  // No bounce (stops on impact)\n
    "},{"location":"manual/game_development/physics_and_collisions/#friction","title":"Friction","text":"

    Applies gradual velocity reduction:

    ball->setFriction(0.0f);  // No friction (slides forever)\nball->setFriction(0.5f);  // Moderate friction\nball->setFriction(1.0f);  // High friction (stops quickly)\n
    "},{"location":"manual/game_development/physics_and_collisions/#world-boundaries","title":"World Boundaries","text":"

    Set the playable area:

    // Set world size (used as default boundaries)\nball->setWorldSize(240, 240);\n\n// Or set custom boundaries\npixelroot32::core::LimitRect limits(10, 10, 230, 230); // Left, Top, Right, Bottom\nball->setLimits(limits);\n
    "},{"location":"manual/game_development/physics_and_collisions/#world-collision-detection","title":"World Collision Detection","text":"

    Check if the actor hit world boundaries:

    void update(unsigned long deltaTime) override {\n    PhysicsActor::update(deltaTime);\n\n    auto collisionInfo = getWorldCollisionInfo();\n\n    if (collisionInfo.left || collisionInfo.right) {\n        // Hit side walls\n    }\n\n    if (collisionInfo.top || collisionInfo.bottom) {\n        // Hit top/bottom walls\n    }\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#collision-system","title":"Collision System","text":"

    The collision system detects when Actors overlap and triggers callbacks.

    "},{"location":"manual/game_development/physics_and_collisions/#collision-layers","title":"Collision Layers","text":"

    Use bit flags to organize actors into groups:

    // Define layers (typically in GameLayers.h)\nnamespace Layers {\n    constexpr uint16_t PLAYER = 0x0001;    // Bit 0\n    constexpr uint16_t ENEMY = 0x0002;     // Bit 1\n    constexpr uint16_t PROJECTILE = 0x0004; // Bit 2\n    constexpr uint16_t WALL = 0x0008;      // Bit 3\n    constexpr uint16_t PICKUP = 0x0010;    // Bit 4\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#setting-up-collisions","title":"Setting Up Collisions","text":"

    Configure each actor's collision layer and mask:

    class PlayerActor : public pixelroot32::core::Actor {\npublic:\n    PlayerActor(float x, float y)\n        : Actor(x, y, 16, 16) {\n        // This actor belongs to the PLAYER layer\n        setCollisionLayer(Layers::PLAYER);\n\n        // This actor can collide with ENEMY, WALL, and PICKUP\n        setCollisionMask(Layers::ENEMY | Layers::WALL | Layers::PICKUP);\n    }\n\n    // ... rest of implementation\n};\n\nclass EnemyActor : public pixelroot32::core::Actor {\npublic:\n    EnemyActor(float x, float y)\n        : Actor(x, y, 16, 16) {\n        // This actor belongs to the ENEMY layer\n        setCollisionLayer(Layers::ENEMY);\n\n        // This actor can collide with PLAYER and PROJECTILE\n        setCollisionMask(Layers::PLAYER | Layers::PROJECTILE);\n    }\n\n    // ... rest of implementation\n};\n
    "},{"location":"manual/game_development/physics_and_collisions/#collision-detection","title":"Collision Detection","text":"

    Collisions are automatically detected by the Scene's CollisionSystem. You handle collisions in onCollision():

    void PlayerActor::onCollision(pixelroot32::core::Actor* other) override {\n    // Check what we collided with\n    if (other->isInLayer(Layers::ENEMY)) {\n        // Hit an enemy - take damage\n        takeDamage();\n    } else if (other->isInLayer(Layers::PICKUP)) {\n        // Hit a pickup - collect it\n        collectPickup(other);\n    } else if (other->isInLayer(Layers::WALL)) {\n        // Hit a wall - stop movement\n        stopMovement();\n    }\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#hitbox","title":"Hitbox","text":"

    Define the collision shape:

    pixelroot32::core::Rect getHitBox() override {\n    // Simple AABB (Axis-Aligned Bounding Box)\n    return {x, y, width, height};\n\n    // Or use a smaller hitbox for more forgiving collisions\n    // return {x + 2, y + 2, width - 4, height - 4};\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#complete-example-bouncing-ball","title":"Complete Example: Bouncing Ball","text":"
    #include <core/PhysicsActor.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\nclass BouncingBall : public pixelroot32::core::PhysicsActor {\npublic:\n    BouncingBall(float x, float y, float radius)\n        : PhysicsActor(x, y, radius * 2, radius * 2) {\n        setRenderLayer(1);\n\n        // Physics setup\n        setRestitution(0.9f);  // Very bouncy\n        setFriction(0.05f);    // Low friction\n        setWorldSize(240, 240); // World boundaries\n\n        // Initial velocity\n        setVelocity(50.0f, -30.0f);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Apply gravity\n        float gravity = 200.0f; // pixels per second squared\n        float dt = deltaTime * 0.001f;\n        setVelocity(vx, vy + gravity * dt);\n\n        // Update physics\n        PhysicsActor::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        int radius = width / 2;\n        renderer.drawFilledCircle(\n            static_cast<int>(x + radius),\n            static_cast<int>(y + radius),\n            radius,\n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Bounce off other objects\n        // (PhysicsActor handles world boundaries automatically)\n    }\n\n    void onWorldCollision() override {\n        // Play bounce sound when hitting walls\n        // (Implementation depends on your audio setup)\n    }\n};\n
    "},{"location":"manual/game_development/physics_and_collisions/#complete-example-platformer-player","title":"Complete Example: Platformer Player","text":"
    class PlatformerPlayer : public pixelroot32::core::PhysicsActor {\nprivate:\n    bool onGround = false;\n    float jumpForce = 250.0f;\n    float moveSpeed = 100.0f;\n\npublic:\n    PlatformerPlayer(float x, float y)\n        : PhysicsActor(x, y, 16, 16) {\n        setRenderLayer(1);\n        setFriction(0.3f);  // Ground friction\n        setWorldSize(240, 240);\n\n        // Collision setup\n        setCollisionLayer(Layers::PLAYER);\n        setCollisionMask(Layers::ENEMY | Layers::PLATFORM);\n    }\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        float dt = deltaTime * 0.001f;\n\n        // Horizontal movement\n        float moveDir = 0.0f;\n        if (input.isButtonDown(Buttons::LEFT)) moveDir -= 1.0f;\n        if (input.isButtonDown(Buttons::RIGHT)) moveDir += 1.0f;\n\n        setVelocity(moveDir * moveSpeed, vy);\n\n        // Apply gravity\n        float gravity = 300.0f;\n        setVelocity(vx, vy + gravity * dt);\n\n        // Jump\n        if (input.isButtonPressed(Buttons::A) && onGround) {\n            setVelocity(vx, -jumpForce);\n            onGround = false;\n        }\n\n        // Update physics\n        PhysicsActor::update(deltaTime);\n\n        // Check if on ground\n        auto collisionInfo = getWorldCollisionInfo();\n        onGround = collisionInfo.bottom;\n\n        // Also check collision with platforms\n        // (This would be handled in onCollision)\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        if (other->isInLayer(Layers::PLATFORM)) {\n            // Land on platform\n            auto platformRect = other->getHitBox();\n            if (y + height <= platformRect.y + 5) { // Within 5 pixels of top\n                y = platformRect.y - height;\n                vy = 0;\n                onGround = true;\n            }\n        } else if (other->isInLayer(Layers::ENEMY)) {\n            // Hit enemy\n            takeDamage();\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x),\n            static_cast<int>(y),\n            width,\n            height,\n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n};\n
    "},{"location":"manual/game_development/physics_and_collisions/#sweep-tests","title":"Sweep Tests","text":"

    For fast-moving projectiles, use sweep tests to detect collisions between positions:

    #include <physics/CollisionPrimitives.h>\n\nbool checkProjectileHit(PhysicsActor* projectile, Actor* target) {\n    // Get previous and current positions\n    float prevX = projectile->x - (projectile->vx * deltaTime * 0.001f);\n    float prevY = projectile->y - (projectile->vy * deltaTime * 0.001f);\n\n    // Create circles for sweep test\n    pixelroot32::physics::Circle startCircle;\n    startCircle.x = prevX + projectile->width / 2;\n    startCircle.y = prevY + projectile->height / 2;\n    startCircle.radius = projectile->width / 2;\n\n    pixelroot32::physics::Circle endCircle;\n    endCircle.x = projectile->x + projectile->width / 2;\n    endCircle.y = projectile->y + projectile->height / 2;\n    endCircle.radius = projectile->width / 2;\n\n    // Get target rectangle\n    auto targetRect = target->getHitBox();\n\n    // Perform sweep test\n    float tHit;\n    if (pixelroot32::physics::sweepCircleVsRect(\n        startCircle, endCircle, targetRect, tHit)) {\n        // Collision detected at time tHit (0.0 = at start, 1.0 = at end)\n        return true;\n    }\n\n    return false;\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/physics_and_collisions/#collision-layers_1","title":"Collision Layers","text":"
    • Plan your layers: Design the layer system before coding
    • Use bit flags: Makes combining layers easy with | operator
    • Keep it simple: Don't create too many layers
    • Document layers: Create a GameLayers.h file with all definitions
    "},{"location":"manual/game_development/physics_and_collisions/#physics","title":"Physics","text":"
    • Use appropriate values: Test gravity, speed, and forces
    • Frame-rate independence: Always use deltaTime
    • Limit world size: Keep boundaries reasonable
    • Test on hardware: Physics may behave differently on ESP32 vs PC
    "},{"location":"manual/game_development/physics_and_collisions/#performance","title":"Performance","text":"
    • Limit active actors: Fewer actors = faster collision checks
    • Use layers efficiently: Reduce unnecessary collision pairs
    • Simple hitboxes: AABB is fast, complex shapes are slow
    • Sweep tests sparingly: Only for fast-moving objects
    "},{"location":"manual/game_development/physics_and_collisions/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/physics_and_collisions/#collision-layer-helper","title":"Collision Layer Helper","text":"
    namespace CollisionLayers {\n    constexpr uint16_t PLAYER = 0x0001;\n    constexpr uint16_t ENEMY = 0x0002;\n    constexpr uint16_t PROJECTILE = 0x0004;\n    constexpr uint16_t WALL = 0x0008;\n    constexpr uint16_t PICKUP = 0x0010;\n\n    // Helper to check if actor is in specific layer\n    bool isPlayer(Actor* actor) {\n        return actor->isInLayer(PLAYER);\n    }\n\n    bool isEnemy(Actor* actor) {\n        return actor->isInLayer(ENEMY);\n    }\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#platform-collision","title":"Platform Collision","text":"
    void PlatformerPlayer::onCollision(Actor* other) override {\n    if (other->isInLayer(Layers::PLATFORM)) {\n        auto platform = other->getHitBox();\n\n        // Check if landing on top of platform\n        float playerBottom = y + height;\n        float platformTop = platform.y;\n\n        if (playerBottom <= platformTop + 5 && vy > 0) {\n            // Land on platform\n            y = platformTop - height;\n            vy = 0;\n            onGround = true;\n        }\n    }\n}\n
    "},{"location":"manual/game_development/physics_and_collisions/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/game_development/physics_and_collisions/#collisions-not-detected","title":"Collisions Not Detected","text":"
    • Verify collision layers and masks overlap
    • Check that actors are added to the scene
    • Ensure Scene::update() is called
    • Verify hitboxes are correct
    "},{"location":"manual/game_development/physics_and_collisions/#physics-too-fastslow","title":"Physics Too Fast/Slow","text":"
    • Adjust values based on deltaTime
    • Check gravity and velocity values
    • Test on actual hardware (ESP32 may run slower)
    "},{"location":"manual/game_development/physics_and_collisions/#objects-passing-through","title":"Objects Passing Through","text":"
    • Use sweep tests for fast objects
    • Increase collision detection frequency
    • Check hitbox sizes match visual size
    "},{"location":"manual/game_development/physics_and_collisions/#next-steps","title":"Next Steps","text":"

    Now that you understand physics and collisions, learn about: - User Interface - Create menus and HUDs - Advanced Graphics - Advanced sprite techniques - Camera and Scrolling - Create scrolling levels

    See also: - API Reference - PhysicsActor - API Reference - CollisionSystem - Manual - Physics Overview - Manual - Collision Detection

    "},{"location":"manual/game_development/scenes_and_entities/","title":"Scenes and Entities","text":"

    Scenes and entities are the foundation of every PixelRoot32 game. This guide teaches you how to organize your game using scenes and create interactive game objects with entities.

    "},{"location":"manual/game_development/scenes_and_entities/#creating-a-scene","title":"Creating a Scene","text":"

    A Scene represents a screen or level in your game. To create a scene, inherit from pixelroot32::core::Scene and implement the three main methods:

    #include <core/Scene.h>\n#include <graphics/Renderer.h>\n\nclass MyGameScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Called once when the scene is initialized\n        // Set up your scene here: create entities, load resources, etc.\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Called every frame\n        // Update game logic here\n\n        // IMPORTANT: Always call parent update to update all entities\n        Scene::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Called every frame to draw\n        // Draw your scene here\n\n        // IMPORTANT: Always call parent draw to draw all entities\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/game_development/scenes_and_entities/#scene-lifecycle","title":"Scene Lifecycle","text":"
    1. init(): Called once when the scene is set as active
    2. Create and initialize entities
    3. Set up game state
    4. Load resources
    5. Configure palettes, audio, etc.

    6. update(deltaTime): Called every frame

    7. Process input
    8. Update game logic
    9. Handle collisions
    10. Must call Scene::update(deltaTime) to update all entities

    11. draw(renderer): Called every frame

    12. Draw background elements
    13. Draw UI elements
    14. Must call Scene::draw(renderer) to draw all entities
    "},{"location":"manual/game_development/scenes_and_entities/#basic-entities","title":"Basic Entities","text":"

    An Entity is any object in your game. To create an entity, inherit from pixelroot32::core::Entity:

    #include <core/Entity.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\nclass SimpleEntity : public pixelroot32::core::Entity {\npublic:\n    SimpleEntity(float x, float y)\n        : Entity(x, y, 16, 16, pixelroot32::core::EntityType::GENERIC) {\n        // Set render layer (0=background, 1=gameplay, 2=UI)\n        setRenderLayer(1);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Update entity logic\n        // For example, move the entity\n        this->x += 1.0f * (deltaTime * 0.001f); // Move right at 1 pixel per second\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw the entity\n        renderer.drawFilledRectangle(\n            static_cast<int>(x), \n            static_cast<int>(y), \n            width, \n            height, \n            pixelroot32::graphics::Color::Red\n        );\n    }\n};\n
    "},{"location":"manual/game_development/scenes_and_entities/#entity-properties","title":"Entity Properties","text":"

    Every entity has these properties:

    • x, y: Position in world space (float)
    • width, height: Dimensions (int)
    • isVisible: If false, draw() is not called
    • isEnabled: If false, update() is not called
    • renderLayer: Which layer to draw on (0, 1, or 2)
    "},{"location":"manual/game_development/scenes_and_entities/#adding-entities-to-a-scene","title":"Adding Entities to a Scene","text":"

    Add entities to your scene in init():

    void MyGameScene::init() override {\n    // Create entities\n    SimpleEntity* entity1 = new SimpleEntity(50, 50);\n    SimpleEntity* entity2 = new SimpleEntity(100, 100);\n\n    // Add them to the scene\n    addEntity(entity1);\n    addEntity(entity2);\n}\n

    The scene automatically manages these entities: - Calls update() on all enabled entities each frame - Calls draw() on all visible entities each frame - Handles cleanup when the scene is destroyed

    "},{"location":"manual/game_development/scenes_and_entities/#actors-entities-with-collisions","title":"Actors: Entities with Collisions","text":"

    An Actor is an entity that can participate in collision detection. Inherit from pixelroot32::core::Actor:

    #include <core/Actor.h>\n#include <physics/CollisionTypes.h>\n\nclass MyActor : public pixelroot32::core::Actor {\npublic:\n    MyActor(float x, float y, int w, int h)\n        : Actor(x, y, w, h) {\n        // Set collision layer (what group this actor belongs to)\n        setCollisionLayer(0x0001); // Example: layer 1\n\n        // Set collision mask (what groups this actor can collide with)\n        setCollisionMask(0x0002); // Example: can collide with layer 2\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Update actor logic\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw the actor\n    }\n\n    // REQUIRED: Define the hitbox for collision detection\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    // REQUIRED: Handle collisions\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // React to collision\n        // For example: take damage, destroy self, etc.\n    }\n};\n
    "},{"location":"manual/game_development/scenes_and_entities/#collision-layers-and-masks","title":"Collision Layers and Masks","text":"

    Collision layers use bit flags to organize actors into groups:

    // Define your layers (typically in a GameLayers.h file)\nnamespace Layers {\n    constexpr uint16_t PLAYER = 0x0001;  // Bit 0\n    constexpr uint16_t ENEMY = 0x0002;  // Bit 1\n    constexpr uint16_t PROJECTILE = 0x0004; // Bit 2\n    constexpr uint16_t WALL = 0x0008;   // Bit 3\n}\n\n// Set up a player actor\nplayer->setCollisionLayer(Layers::PLAYER);\nplayer->setCollisionMask(Layers::ENEMY | Layers::WALL); // Can collide with enemies and walls\n\n// Set up an enemy actor\nenemy->setCollisionLayer(Layers::ENEMY);\nenemy->setCollisionMask(Layers::PLAYER | Layers::PROJECTILE); // Can collide with player and projectiles\n

    The collision system only checks collisions between actors whose layers and masks overlap. This is much more efficient than checking every pair.

    "},{"location":"manual/game_development/scenes_and_entities/#scene-management","title":"Scene Management","text":""},{"location":"manual/game_development/scenes_and_entities/#setting-the-active-scene","title":"Setting the Active Scene","text":"

    From your main code, set the active scene:

    MyGameScene gameScene;\n\nvoid setup() {\n    engine.init();\n    gameScene.init();\n    engine.setScene(&gameScene); // Set as active scene\n}\n
    "},{"location":"manual/game_development/scenes_and_entities/#switching-scenes","title":"Switching Scenes","text":"

    To switch to a different scene:

    MenuScene menuScene;\nGameScene gameScene;\n\nvoid switchToGame() {\n    gameScene.init();\n    engine.setScene(&gameScene); // Replaces current scene\n}\n
    "},{"location":"manual/game_development/scenes_and_entities/#scene-stack-pushpop","title":"Scene Stack (Push/Pop)","text":"

    For menus and pause screens, use the scene stack:

    // Push a pause menu (game scene stays in background)\nvoid pauseGame() {\n    pauseMenu.init();\n    engine.getCurrentScene()->getSceneManager().pushScene(&pauseMenu);\n}\n\n// Pop the pause menu (resume game)\nvoid resumeGame() {\n    engine.getCurrentScene()->getSceneManager().popScene();\n}\n

    Note: Scene stack management is handled internally by the Engine's SceneManager. You typically access it through engine.getCurrentScene().

    "},{"location":"manual/game_development/scenes_and_entities/#complete-example","title":"Complete Example","text":"

    Here's a complete example of a scene with multiple entities:

    #include <core/Scene.h>\n#include <core/Entity.h>\n#include <core/Actor.h>\n#include <graphics/Renderer.h>\n#include <graphics/Color.h>\n\n// A simple moving entity\nclass MovingBox : public pixelroot32::core::Entity {\npublic:\n    MovingBox(float x, float y) \n        : Entity(x, y, 20, 20, pixelroot32::core::EntityType::GENERIC),\n          speedX(50.0f), speedY(30.0f) {\n        setRenderLayer(1);\n    }\n\n    void update(unsigned long deltaTime) override {\n        float dt = deltaTime * 0.001f; // Convert to seconds\n\n        x += speedX * dt;\n        y += speedY * dt;\n\n        // Bounce off screen edges\n        if (x < 0 || x > 220) speedX = -speedX;\n        if (y < 0 || y > 220) speedY = -speedY;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x), \n            static_cast<int>(y), \n            width, \n            height, \n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n\nprivate:\n    float speedX, speedY;\n};\n\n// A simple actor that can collide\nclass CollidableBox : public pixelroot32::core::Actor {\npublic:\n    CollidableBox(float x, float y)\n        : Actor(x, y, 30, 30) {\n        setRenderLayer(1);\n        setCollisionLayer(0x0001);\n        setCollisionMask(0x0001);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Static actor, no movement\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x), \n            static_cast<int>(y), \n            width, \n            height, \n            pixelroot32::graphics::Color::Yellow\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Change color when collided\n        // (In a real game, you'd handle collision logic here)\n    }\n};\n\n// The scene\nclass ExampleScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Create and add entities\n        addEntity(new MovingBox(50, 50));\n        addEntity(new MovingBox(150, 100));\n        addEntity(new CollidableBox(100, 100));\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime); // Update all entities\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // Draw background\n        renderer.drawFilledRectangle(0, 0, 240, 240, \n            pixelroot32::graphics::Color::Black);\n\n        Scene::draw(renderer); // Draw all entities\n    }\n};\n
    "},{"location":"manual/game_development/scenes_and_entities/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/scenes_and_entities/#entity-management","title":"Entity Management","text":"
    • Pre-allocate entities: Create entities in init(), not in update()
    • Reuse entities: Instead of creating/destroying, enable/disable entities
    • Limit entity count: MAX_ENTITIES = 32 per scene
    • Use object pooling: For frequently created/destroyed entities (projectiles, particles)
    "},{"location":"manual/game_development/scenes_and_entities/#scene-organization","title":"Scene Organization","text":"
    • One scene per screen: Menu, game, game over, etc.
    • Keep scenes focused: Each scene should have a single responsibility
    • Initialize in init(): Don't do heavy work in the constructor
    • Clean up properly: Remove entities when switching scenes
    "},{"location":"manual/game_development/scenes_and_entities/#collision-layers","title":"Collision Layers","text":"
    • Plan your layers: Design your layer system before coding
    • Use bit flags: Makes layer combinations easy
    • Keep it simple: Don't over-complicate with too many layers
    • Document your layers: Create a GameLayers.h file
    "},{"location":"manual/game_development/scenes_and_entities/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/scenes_and_entities/#entity-pool-pattern","title":"Entity Pool Pattern","text":"

    For entities that are frequently created and destroyed (like projectiles):

    class ProjectilePool {\n    static const int POOL_SIZE = 10;\n    ProjectileActor pool[POOL_SIZE];\n\npublic:\n    ProjectileActor* getAvailable() {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (!pool[i].isActive) {\n                pool[i].isActive = true;\n                return &pool[i];\n            }\n        }\n        return nullptr; // Pool exhausted\n    }\n};\n
    "},{"location":"manual/game_development/scenes_and_entities/#entity-factory-pattern","title":"Entity Factory Pattern","text":"

    Create entities through factory functions:

    Entity* createEnemy(EnemyType type, float x, float y) {\n    switch (type) {\n        case EnemyType::BASIC:\n            return new BasicEnemy(x, y);\n        case EnemyType::FAST:\n            return new FastEnemy(x, y);\n        // ...\n    }\n}\n
    "},{"location":"manual/game_development/scenes_and_entities/#next-steps","title":"Next Steps","text":"

    Now that you understand scenes and entities, learn about: - Basic Rendering - Draw sprites, text, and primitives - Input and Control - Handle user input - Physics and Collisions - Advanced collision handling

    See also: - Fundamental Concepts - API Reference - Scene - API Reference - Entity - API Reference - Actor

    "},{"location":"manual/game_development/user_interface/","title":"User Interface","text":"

    PixelRoot32 provides a complete UI system for creating menus, HUDs, and interface elements. This guide covers all UI components and layout systems.

    "},{"location":"manual/game_development/user_interface/#ui-elements","title":"UI Elements","text":"

    All UI elements inherit from UIElement, which itself inherits from Entity. This means UI elements can be added to scenes just like any other entity.

    "},{"location":"manual/game_development/user_interface/#uilabel","title":"UILabel","text":"

    Display text on screen:

    #include <graphics/ui/UILabel.h>\n\n// Create a label\npixelroot32::graphics::ui::UILabel* scoreLabel = new pixelroot32::graphics::ui::UILabel(\n    \"Score: 0\",                    // text\n    10,                            // x position\n    10,                            // y position\n    pixelroot32::graphics::Color::White,  // color\n    1                              // size multiplier\n);\n\n// Add to scene\naddEntity(scoreLabel);\n\n// Update text dynamically\nscoreLabel->setText(\"Score: 100\");\n\n// Center horizontally\nscoreLabel->centerX(240); // Screen width\n
    "},{"location":"manual/game_development/user_interface/#uibutton","title":"UIButton","text":"

    Create clickable buttons:

    #include <graphics/ui/UIButton.h>\n\n// Create a button\npixelroot32::graphics::ui::UIButton* startButton = new pixelroot32::graphics::ui::UIButton(\n    \"Start Game\",                  // text\n    4,                             // navigation index\n    50,                            // x position\n    100,                           // y position\n    140,                           // width\n    30,                            // height\n    []() {                         // callback function\n        // Button clicked - start game\n        startGame();\n    }\n);\n\n// Configure style\nstartButton->setStyle(\n    pixelroot32::graphics::Color::White,   // text color\n    pixelroot32::graphics::Color::Blue,    // background color\n    true                                    // draw background\n);\n\n// Add to scene\naddEntity(startButton);\n
    "},{"location":"manual/game_development/user_interface/#uicheckbox","title":"UICheckBox","text":"

    Create interactive checkboxes:

    #include <graphics/ui/UICheckBox.h>\n\n// Create a checkbox\npixelroot32::graphics::ui::UICheckBox* soundCheckbox = new pixelroot32::graphics::ui::UICheckBox(\n    \"Enable Sound\",                // text\n    4,                             // navigation index\n    50,                            // x position\n    140,                           // y position\n    140,                           // width\n    20,                            // height\n    true,                          // initial checked state\n    [](bool checked) {             // callback function\n        // Checkbox state changed\n        setSoundEnabled(checked);\n    },\n    1                              // font size\n);\n\n// Configure style\nsoundCheckbox->setStyle(\n    pixelroot32::graphics::Color::White,   // text color\n    pixelroot32::graphics::Color::Blue,    // background color\n    false                                  // draw background\n);\n\n// Add to scene\naddEntity(soundCheckbox);\n
    "},{"location":"manual/game_development/user_interface/#uipanel","title":"UIPanel","text":"

    Create visual containers with background and border:

    #include <graphics/ui/UIPanel.h>\n\n// Create a panel\npixelroot32::graphics::ui::UIPanel* dialog = new pixelroot32::graphics::ui::UIPanel(\n    50,   // x\n    50,   // y\n    140,  // width\n    140   // height\n);\n\n// Configure appearance\ndialog->setBackgroundColor(pixelroot32::graphics::Color::Black);\ndialog->setBorderColor(pixelroot32::graphics::Color::White);\ndialog->setBorderWidth(2);\n\n// Add content (typically a layout)\ndialog->setChild(menuLayout);\n\n// Add to scene\naddEntity(dialog);\n
    "},{"location":"manual/game_development/user_interface/#layouts","title":"Layouts","text":"

    Layouts automatically organize UI elements, eliminating the need for manual position calculations.

    "},{"location":"manual/game_development/user_interface/#uiverticallayout","title":"UIVerticalLayout","text":"

    Organize elements vertically with automatic scrolling:

    #include <graphics/ui/UIVerticalLayout.h>\n\n// Create vertical layout\npixelroot32::graphics::ui::UIVerticalLayout* menu = new pixelroot32::graphics::ui::UIVerticalLayout(\n    10,   // x\n    60,   // y\n    220,  // width\n    160   // height (viewport)\n);\n\n// Configure layout\nmenu->setPadding(5);        // Internal padding\nmenu->setSpacing(6);        // Space between elements\nmenu->setScrollEnabled(true); // Enable scrolling\n\n// Set navigation buttons\nmenu->setNavigationButtons(0, 1); // UP=0, DOWN=1\n\n// Set button styles\nmenu->setButtonStyle(\n    pixelroot32::graphics::Color::White,  // selected text\n    pixelroot32::graphics::Color::Cyan,    // selected background\n    pixelroot32::graphics::Color::White,  // unselected text\n    pixelroot32::graphics::Color::Black   // unselected background\n);\n\n// Add buttons (no manual positioning needed!)\nfor (int i = 0; i < 10; i++) {\n    UIButton* btn = new UIButton(\n        \"Option \" + std::to_string(i),\n        i,\n        0, 0,  // Position ignored - layout handles it\n        200, 20,\n        [i]() { handleOption(i); }\n    );\n    menu->addElement(btn);\n}\n\n// Add layout to scene\nmenu->setRenderLayer(2); // UI layer\naddEntity(menu);\n
    "},{"location":"manual/game_development/user_interface/#uihorizontallayout","title":"UIHorizontalLayout","text":"

    Organize elements horizontally:

    #include <graphics/ui/UIHorizontalLayout.h>\n\n// Create horizontal layout (menu bar)\npixelroot32::graphics::ui::UIHorizontalLayout* menuBar = new pixelroot32::graphics::ui::UIHorizontalLayout(\n    0,    // x\n    0,    // y\n    240,  // width\n    30    // height\n);\n\nmenuBar->setPadding(5);\nmenuBar->setSpacing(4);\nmenuBar->setScrollEnabled(true);\nmenuBar->setNavigationButtons(2, 3); // LEFT=2, RIGHT=3\n\n// Add menu items\nmenuBar->addElement(new UIButton(\"File\", 0, 0, 0, 60, 20, []() {}));\nmenuBar->addElement(new UIButton(\"Edit\", 1, 0, 0, 60, 20, []() {}));\nmenuBar->addElement(new UIButton(\"View\", 2, 0, 0, 60, 20, []() {}));\n\naddEntity(menuBar);\n
    "},{"location":"manual/game_development/user_interface/#uigridlayout","title":"UIGridLayout","text":"

    Organize elements in a grid (matrix):

    #include <graphics/ui/UIGridLayout.h>\n\n// Create grid layout (inventory)\npixelroot32::graphics::ui::UIGridLayout* inventory = new pixelroot32::graphics::ui::UIGridLayout(\n    10,   // x\n    60,   // y\n    220,  // width\n    160   // height\n);\n\ninventory->setColumns(4);  // 4 columns\ninventory->setPadding(5);\ninventory->setSpacing(4);\ninventory->setNavigationButtons(0, 1, 2, 3); // UP, DOWN, LEFT, RIGHT\n\n// Add items (automatically arranged in grid)\nfor (int i = 0; i < 16; i++) {\n    UIButton* item = new UIButton(\n        \"Item \" + std::to_string(i),\n        i,\n        0, 0,  // Position ignored\n        50, 50,\n        [i]() { useItem(i); }\n    );\n    inventory->addElement(item);\n}\n\naddEntity(inventory);\n
    "},{"location":"manual/game_development/user_interface/#uianchorlayout","title":"UIAnchorLayout","text":"

    Position elements at fixed screen positions (perfect for HUDs):

    #include <graphics/ui/UIAnchorLayout.h>\n\n// Create anchor layout for HUD\npixelroot32::graphics::ui::UIAnchorLayout* hud = new pixelroot32::graphics::ui::UIAnchorLayout(\n    0,    // x\n    0,    // y\n    240,  // screen width\n    240   // screen height\n);\n\nhud->setScreenSize(240, 240);\n\n// Add HUD elements at different anchor points\nUILabel* scoreLabel = new UILabel(\"Score: 0\", 0, 0, Color::White, 1);\nUILabel* livesLabel = new UILabel(\"Lives: 3\", 0, 0, Color::White, 1);\n\nhud->addElement(scoreLabel, pixelroot32::graphics::ui::Anchor::TOP_LEFT);\nhud->addElement(livesLabel, pixelroot32::graphics::ui::Anchor::TOP_RIGHT);\n\naddEntity(hud);\n

    Available Anchors: - TOP_LEFT, TOP_RIGHT, TOP_CENTER - BOTTOM_LEFT, BOTTOM_RIGHT, BOTTOM_CENTER - LEFT_CENTER, RIGHT_CENTER - CENTER

    "},{"location":"manual/game_development/user_interface/#uipaddingcontainer","title":"UIPaddingContainer","text":"

    Add padding around a single element:

    #include <graphics/ui/UIPaddingContainer.h>\n\n// Create padding container\npixelroot32::graphics::ui::UIPaddingContainer* container = new pixelroot32::graphics::ui::UIPaddingContainer(\n    10,   // x\n    10,   // y\n    200,  // width\n    100   // height\n);\n\n// Set uniform padding\ncontainer->setPadding(10);\n\n// Or set asymmetric padding\ncontainer->setPadding(5, 15, 10, 10); // left, right, top, bottom\n\n// Add child element\ncontainer->setChild(button);\n\naddEntity(container);\n
    "},{"location":"manual/game_development/user_interface/#navigation","title":"Navigation","text":"

    Layouts handle D-pad navigation automatically:

    "},{"location":"manual/game_development/user_interface/#vertical-navigation","title":"Vertical Navigation","text":"
    verticalLayout->setNavigationButtons(Buttons::UP, Buttons::DOWN);\n\n// Layout automatically:\n// - Highlights selected button\n// - Scrolls to keep selected button visible\n// - Handles wrapping (optional)\n
    "},{"location":"manual/game_development/user_interface/#horizontal-navigation","title":"Horizontal Navigation","text":"
    horizontalLayout->setNavigationButtons(Buttons::LEFT, Buttons::RIGHT);\n
    "},{"location":"manual/game_development/user_interface/#grid-navigation","title":"Grid Navigation","text":"
    gridLayout->setNavigationButtons(Buttons::UP, Buttons::DOWN, Buttons::LEFT, Buttons::RIGHT);\n\n// Layout automatically:\n// - Handles 4-direction navigation\n// - Wraps around edges\n// - Updates selection\n
    "},{"location":"manual/game_development/user_interface/#manual-selection","title":"Manual Selection","text":"
    // Set selected element programmatically\nlayout->setSelectedIndex(2);\n\n// Get selected element\nint selected = layout->getSelectedIndex();\nUIElement* element = layout->getSelectedElement();\n
    "},{"location":"manual/game_development/user_interface/#complete-example-main-menu","title":"Complete Example: Main Menu","text":"
    #include <core/Scene.h>\n#include <graphics/ui/UIVerticalLayout.h>\n#include <graphics/ui/UIButton.h>\n#include <graphics/ui/UILabel.h>\n#include <graphics/ui/UIPanel.h>\n\nclass MainMenuScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIVerticalLayout* menuLayout;\n    pixelroot32::graphics::ui::UIPanel* menuPanel;\n\npublic:\n    void init() override {\n        // Create panel\n        menuPanel = new pixelroot32::graphics::ui::UIPanel(40, 40, 160, 160);\n        menuPanel->setBackgroundColor(pixelroot32::graphics::Color::Black);\n        menuPanel->setBorderColor(pixelroot32::graphics::Color::White);\n        menuPanel->setBorderWidth(2);\n        menuPanel->setRenderLayer(2);\n        addEntity(menuPanel);\n\n        // Create layout inside panel\n        menuLayout = new pixelroot32::graphics::ui::UIVerticalLayout(0, 0, 160, 160);\n        menuLayout->setPadding(10);\n        menuLayout->setSpacing(8);\n        menuLayout->setNavigationButtons(0, 1);\n        menuLayout->setButtonStyle(\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Cyan,\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Black\n        );\n\n        // Add title\n        pixelroot32::graphics::ui::UILabel* title = new pixelroot32::graphics::ui::UILabel(\n            \"GAME MENU\", 0, 0, pixelroot32::graphics::Color::Yellow, 2\n        );\n        menuLayout->addElement(title);\n\n        // Add menu buttons\n        menuLayout->addElement(new pixelroot32::graphics::ui::UIButton(\n            \"Start Game\", 0, 0, 0, 140, 25, []() { startGame(); }\n        ));\n\n        menuLayout->addElement(new pixelroot32::graphics::ui::UIButton(\n            \"Options\", 1, 0, 0, 140, 25, []() { showOptions(); }\n        ));\n\n        menuLayout->addElement(new pixelroot32::graphics::ui::UIButton(\n            \"Quit\", 2, 0, 0, 140, 25, []() { quitGame(); }\n        ));\n\n        menuPanel->setChild(menuLayout);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Handle input for layout navigation\n        auto& input = engine.getInputManager();\n        menuLayout->handleInput(input);\n\n        Scene::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"manual/game_development/user_interface/#complete-example-hud","title":"Complete Example: HUD","text":"
    class GameHUD : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::ui::UIAnchorLayout* hud;\n    pixelroot32::graphics::ui::UILabel* scoreLabel;\n    pixelroot32::graphics::ui::UILabel* livesLabel;\n    pixelroot32::graphics::ui::UILabel* healthLabel;\n\npublic:\n    GameHUD()\n        : Entity(0, 0, 240, 240, pixelroot32::core::EntityType::UI_ELEMENT) {\n        setRenderLayer(2); // UI layer\n\n        // Create HUD layout\n        hud = new pixelroot32::graphics::ui::UIAnchorLayout(0, 0, 240, 240);\n        hud->setScreenSize(240, 240);\n\n        // Create labels\n        scoreLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Score: 0\", 0, 0, pixelroot32::graphics::Color::White, 1\n        );\n\n        livesLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Lives: 3\", 0, 0, pixelroot32::graphics::Color::White, 1\n        );\n\n        healthLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Health: 100%\", 0, 0, pixelroot32::graphics::Color::Green, 1\n        );\n\n        // Position labels\n        hud->addElement(scoreLabel, pixelroot32::graphics::ui::Anchor::TOP_LEFT);\n        hud->addElement(livesLabel, pixelroot32::graphics::ui::Anchor::TOP_RIGHT);\n        hud->addElement(healthLabel, pixelroot32::graphics::ui::Anchor::BOTTOM_CENTER);\n    }\n\n    void update(unsigned long deltaTime) override {\n        // Update HUD text (example)\n        char buffer[32];\n        snprintf(buffer, sizeof(buffer), \"Score: %d\", currentScore);\n        scoreLabel->setText(buffer);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        // HUD draws itself through its layout\n    }\n\n    // Add HUD to scene\n    void addToScene(Scene* scene) {\n        scene->addEntity(hud);\n    }\n};\n
    "},{"location":"manual/game_development/user_interface/#best-practices","title":"Best Practices","text":""},{"location":"manual/game_development/user_interface/#organization","title":"Organization","text":"
    • Use render layer 2: Keep all UI on the top layer
    • Group related elements: Use panels to group menu items
    • Separate HUD from menus: Use different layouts for different UI types
    • Reuse layouts: Create layout factories for common patterns
    "},{"location":"manual/game_development/user_interface/#performance","title":"Performance","text":"
    • Layouts use viewport culling: Only visible elements are rendered
    • Minimize text updates: Updating text has overhead
    • Use appropriate layouts: Choose the right layout for your needs
    • Limit element count: Too many elements can impact performance
    "},{"location":"manual/game_development/user_interface/#navigation_1","title":"Navigation","text":"
    • Set navigation buttons: Configure D-pad navigation for layouts
    • Handle input in update(): Check for button presses to trigger actions
    • Provide visual feedback: Selected buttons should be clearly visible
    • Test navigation flow: Ensure navigation feels responsive
    "},{"location":"manual/game_development/user_interface/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/game_development/user_interface/#menu-system","title":"Menu System","text":"
    class MenuSystem {\n    UIVerticalLayout* currentMenu;\n\npublic:\n    void showMainMenu() {\n        currentMenu = createMainMenu();\n        scene->addEntity(currentMenu);\n    }\n\n    void showPauseMenu() {\n        currentMenu = createPauseMenu();\n        scene->addEntity(currentMenu);\n    }\n\n    void hideMenu() {\n        if (currentMenu) {\n            scene->removeEntity(currentMenu);\n            currentMenu = nullptr;\n        }\n    }\n};\n
    "},{"location":"manual/game_development/user_interface/#dynamic-ui-updates","title":"Dynamic UI Updates","text":"
    void updateHUD(int score, int lives, int health) {\n    char buffer[32];\n\n    snprintf(buffer, sizeof(buffer), \"Score: %d\", score);\n    scoreLabel->setText(buffer);\n\n    snprintf(buffer, sizeof(buffer), \"Lives: %d\", lives);\n    livesLabel->setText(buffer);\n\n    snprintf(buffer, sizeof(buffer), \"Health: %d%%\", health);\n    healthLabel->setText(buffer);\n}\n
    "},{"location":"manual/game_development/user_interface/#next-steps","title":"Next Steps","text":"

    Now that you understand the UI system, you've completed the core game development topics. Continue with: - Advanced Graphics - Advanced sprite techniques - Camera and Scrolling - Create scrolling levels - Performance Tuning - Improve performance

    See also: - API Reference - UIElement - API Reference - UIButton - API Reference - UI Layouts - Manual - UI Overview

    "},{"location":"manual/optimization/extensibility/","title":"Extensibility","text":"

    PixelRoot32 is designed to be extensible. This guide covers how to create custom drivers, audio backends, and extend existing systems.

    "},{"location":"manual/optimization/extensibility/#creating-custom-display-drivers","title":"Creating Custom Display Drivers","text":"

    To support a new display, implement the DrawSurface interface.

    "},{"location":"manual/optimization/extensibility/#drawsurface-interface","title":"DrawSurface Interface","text":"
    #include <graphics/DrawSurface.h>\n\nclass MyCustomDrawer : public pixelroot32::graphics::DrawSurface {\npublic:\n    // Required methods\n    void init() override;\n    void setRotation(uint8_t rotation) override;\n    void clearBuffer() override;\n    void sendBuffer() override;\n\n    // Drawing primitives\n    void drawPixel(int x, int y, uint16_t color) override;\n    void drawLine(int x1, int y1, int x2, int y2, uint16_t color) override;\n    void drawRectangle(int x, int y, int width, int height, uint16_t color) override;\n    void drawFilledRectangle(int x, int y, int width, int height, uint16_t color) override;\n    void drawCircle(int x, int y, int radius, uint16_t color) override;\n    void drawFilledCircle(int x, int y, int radius, uint16_t color) override;\n    void drawBitmap(int x, int y, int width, int height, const uint8_t* bitmap, uint16_t color) override;\n\n    // Text (deprecated, but must implement)\n    void drawText(const char* text, int16_t x, int16_t y, uint16_t color, uint8_t size) override;\n    void drawTextCentered(const char* text, int16_t y, uint16_t color, uint8_t size) override;\n\n    // State management\n    void setTextColor(uint16_t color) override;\n    void setTextSize(uint8_t size) override;\n    void setCursor(int16_t x, int16_t y) override;\n    void setContrast(uint8_t level) override;\n    void setDisplaySize(int w, int h) override;\n\n    // Utilities\n    uint16_t color565(uint8_t r, uint8_t g, uint8_t b) override;\n    bool processEvents() override;\n    void present() override;\n};\n
    "},{"location":"manual/optimization/extensibility/#example-simple-custom-drawer","title":"Example: Simple Custom Drawer","text":"
    #include <graphics/DrawSurface.h>\n\nclass SimpleDrawer : public pixelroot32::graphics::DrawSurface {\nprivate:\n    uint16_t* framebuffer;\n    int width, height;\n\npublic:\n    SimpleDrawer(int w, int h) : width(w), height(h) {\n        framebuffer = new uint16_t[w * h];\n    }\n\n    ~SimpleDrawer() {\n        delete[] framebuffer;\n    }\n\n    void init() override {\n        // Initialize your display hardware\n        // Clear framebuffer\n        clearBuffer();\n    }\n\n    void clearBuffer() override {\n        for (int i = 0; i < width * height; i++) {\n            framebuffer[i] = 0x0000; // Black\n        }\n    }\n\n    void sendBuffer() override {\n        // Send framebuffer to display\n        // Implementation depends on your hardware\n    }\n\n    void drawPixel(int x, int y, uint16_t color) override {\n        if (x >= 0 && x < width && y >= 0 && y < height) {\n            framebuffer[y * width + x] = color;\n        }\n    }\n\n    void drawFilledRectangle(int x, int y, int w, int h, uint16_t color) override {\n        for (int py = y; py < y + h; py++) {\n            for (int px = x; px < x + w; px++) {\n                drawPixel(px, py, color);\n            }\n        }\n    }\n\n    // Implement other required methods...\n    // (See TFT_eSPI_Drawer or SDL2_Drawer for reference implementations)\n};\n
    "},{"location":"manual/optimization/extensibility/#integrating-custom-driver","title":"Integrating Custom Driver","text":"
    // Create custom drawer\nSimpleDrawer* customDrawer = new SimpleDrawer(240, 240);\n\n// Create renderer with custom drawer\npixelroot32::graphics::DisplayConfig config(\n    pixelroot32::graphics::DisplayType::NONE, // Use NONE for custom\n    0, 240, 240\n);\n\n// You'll need to modify Engine to accept custom DrawSurface\n// Or create a custom Engine wrapper\n
    "},{"location":"manual/optimization/extensibility/#creating-custom-audio-backends","title":"Creating Custom Audio Backends","text":"

    Implement the AudioBackend interface for custom audio hardware.

    "},{"location":"manual/optimization/extensibility/#audiobackend-interface","title":"AudioBackend Interface","text":"
    #include <audio/AudioBackend.h>\n\nclass MyCustomAudioBackend : public pixelroot32::audio::AudioBackend {\npublic:\n    // Required methods\n    void init() override;\n    void start() override;\n    void stop() override;\n    uint32_t getSampleRate() const override;\n\n    // Audio generation\n    int16_t generateSample() override;\n\n    // Channel management\n    void setChannelWave(int channel, pixelroot32::audio::WaveType type, float frequency, float duty) override;\n    void setChannelVolume(int channel, float volume) override;\n    void stopChannel(int channel) override;\n};\n
    "},{"location":"manual/optimization/extensibility/#example-custom-audio-backend","title":"Example: Custom Audio Backend","text":"
    #include <audio/AudioBackend.h>\n\nclass CustomAudioBackend : public pixelroot32::audio::AudioBackend {\nprivate:\n    uint32_t sampleRate;\n    float phase[4] = {0, 0, 0, 0}; // 4 channels\n    float frequency[4] = {0, 0, 0, 0};\n    float volume[4] = {0, 0, 0, 0};\n    pixelroot32::audio::WaveType waveType[4];\n\npublic:\n    CustomAudioBackend(uint32_t rate) : sampleRate(rate) {\n        for (int i = 0; i < 4; i++) {\n            waveType[i] = pixelroot32::audio::WaveType::PULSE;\n            volume[i] = 0.0f;\n        }\n    }\n\n    void init() override {\n        // Initialize your audio hardware\n    }\n\n    void start() override {\n        // Start audio output\n    }\n\n    void stop() override {\n        // Stop audio output\n    }\n\n    uint32_t getSampleRate() const override {\n        return sampleRate;\n    }\n\n    int16_t generateSample() override {\n        float sample = 0.0f;\n\n        for (int ch = 0; ch < 4; ch++) {\n            if (frequency[ch] > 0 && volume[ch] > 0) {\n                float phaseIncrement = frequency[ch] / sampleRate;\n                phase[ch] += phaseIncrement;\n                if (phase[ch] >= 1.0f) phase[ch] -= 1.0f;\n\n                float channelSample = 0.0f;\n                switch (waveType[ch]) {\n                    case pixelroot32::audio::WaveType::PULSE:\n                        channelSample = (phase[ch] < 0.5f) ? 1.0f : -1.0f;\n                        break;\n                    case pixelroot32::audio::WaveType::TRIANGLE:\n                        channelSample = (phase[ch] < 0.5f) ? \n                            (phase[ch] * 4.0f - 1.0f) : \n                            (3.0f - phase[ch] * 4.0f);\n                        break;\n                    // ... other wave types\n                }\n\n                sample += channelSample * volume[ch];\n            }\n        }\n\n        // Clamp and convert to int16_t\n        if (sample > 1.0f) sample = 1.0f;\n        if (sample < -1.0f) sample = -1.0f;\n        return static_cast<int16_t>(sample * 32767.0f);\n    }\n\n    void setChannelWave(int ch, pixelroot32::audio::WaveType type, \n                       float freq, float duty) override {\n        if (ch >= 0 && ch < 4) {\n            waveType[ch] = type;\n            frequency[ch] = freq;\n        }\n    }\n\n    void setChannelVolume(int ch, float vol) override {\n        if (ch >= 0 && ch < 4) {\n            volume[ch] = vol;\n        }\n    }\n\n    void stopChannel(int ch) override {\n        if (ch >= 0 && ch < 4) {\n            frequency[ch] = 0.0f;\n            volume[ch] = 0.0f;\n        }\n    }\n};\n
    "},{"location":"manual/optimization/extensibility/#extending-existing-systems","title":"Extending Existing Systems","text":""},{"location":"manual/optimization/extensibility/#custom-entity-types","title":"Custom Entity Types","text":"

    Create specialized entity types:

    class PowerUpActor : public pixelroot32::core::Actor {\nprivate:\n    PowerUpType type;\n    float lifetime = 5.0f; // 5 seconds\n\npublic:\n    PowerUpActor(float x, float y, PowerUpType t)\n        : Actor(x, y, 8, 8), type(t) {\n        setRenderLayer(1);\n        setCollisionLayer(Layers::POWERUP);\n        setCollisionMask(Layers::PLAYER);\n    }\n\n    void update(unsigned long deltaTime) override {\n        lifetime -= deltaTime * 0.001f;\n        if (lifetime <= 0) {\n            isEnabled = false;\n            isVisible = false;\n        }\n\n        // Animate (bob up and down)\n        y += sin(millis() * 0.005f) * 0.5f;\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        if (other->isInLayer(Layers::PLAYER)) {\n            applyPowerUp(other);\n            isEnabled = false;\n            isVisible = false;\n        }\n    }\n\nprivate:\n    void applyPowerUp(pixelroot32::core::Actor* player) {\n        switch (type) {\n            case PowerUpType::SPEED:\n                // Increase player speed\n                break;\n            case PowerUpType::HEALTH:\n                // Restore health\n                break;\n            // ...\n        }\n    }\n};\n
    "},{"location":"manual/optimization/extensibility/#custom-ui-layouts","title":"Custom UI Layouts","text":"

    Create new layout types:

    #include <graphics/ui/UILayout.h>\n\nclass UICircularLayout : public pixelroot32::graphics::ui::UILayout {\nprivate:\n    float radius;\n    float startAngle;\n\npublic:\n    UICircularLayout(float x, float y, float w, float h, float r)\n        : UILayout(x, y, w, h), radius(r), startAngle(0.0f) {\n    }\n\n    void updateLayout() override {\n        int count = elements.size();\n        float angleStep = 360.0f / count;\n\n        for (size_t i = 0; i < elements.size(); i++) {\n            float angle = startAngle + (i * angleStep);\n            float rad = angle * M_PI / 180.0f;\n\n            float elementX = x + (radius * cos(rad)) - (elements[i]->width / 2);\n            float elementY = y + (radius * sin(rad)) - (elements[i]->height / 2);\n\n            elements[i]->x = elementX;\n            elements[i]->y = elementY;\n        }\n    }\n\n    void handleInput(const pixelroot32::input::InputManager& input) override {\n        // Implement circular navigation\n        // ...\n    }\n};\n
    "},{"location":"manual/optimization/extensibility/#custom-collision-primitives","title":"Custom Collision Primitives","text":"

    Extend collision system with new shapes:

    // Add to your game code (not engine modification)\nstruct Triangle {\n    float x1, y1, x2, y2, x3, y3;\n};\n\nbool intersects(const Triangle& tri, const pixelroot32::core::Rect& rect) {\n    // Implement triangle-rectangle intersection\n    // ...\n    return false;\n}\n\nbool intersects(const Triangle& tri1, const Triangle& tri2) {\n    // Implement triangle-triangle intersection\n    // ...\n    return false;\n}\n
    "},{"location":"manual/optimization/extensibility/#best-practices","title":"Best Practices","text":""},{"location":"manual/optimization/extensibility/#maintain-compatibility","title":"Maintain Compatibility","text":"
    • Don't break existing APIs: Extend, don't modify
    • Use inheritance: Inherit from base classes
    • Follow patterns: Match existing code patterns
    • Document extensions: Comment your custom code
    "},{"location":"manual/optimization/extensibility/#testing","title":"Testing","text":"
    • Test on both platforms: ESP32 and Native
    • Test edge cases: Boundary conditions, null pointers
    • Performance testing: Ensure extensions don't hurt performance
    • Memory testing: Check for leaks with custom code
    "},{"location":"manual/optimization/extensibility/#documentation","title":"Documentation","text":"
    • Comment your code: Explain why, not just what
    • Provide examples: Show how to use your extensions
    • Document limitations: State what doesn't work
    • Version compatibility: Note which engine version
    "},{"location":"manual/optimization/extensibility/#common-extension-patterns","title":"Common Extension Patterns","text":""},{"location":"manual/optimization/extensibility/#factory-pattern","title":"Factory Pattern","text":"
    class EntityFactory {\npublic:\n    static pixelroot32::core::Entity* createEnemy(EnemyType type, float x, float y) {\n        switch (type) {\n            case EnemyType::BASIC:\n                return new BasicEnemy(x, y);\n            case EnemyType::FAST:\n                return new FastEnemy(x, y);\n            case EnemyType::TANK:\n                return new TankEnemy(x, y);\n            default:\n                return nullptr;\n        }\n    }\n\n    static pixelroot32::core::Entity* createPowerUp(PowerUpType type, float x, float y) {\n        return new PowerUpActor(x, y, type);\n    }\n};\n
    "},{"location":"manual/optimization/extensibility/#strategy-pattern","title":"Strategy Pattern","text":"
    class MovementStrategy {\npublic:\n    virtual void update(pixelroot32::core::Actor* actor, unsigned long deltaTime) = 0;\n};\n\nclass LinearMovement : public MovementStrategy {\npublic:\n    void update(pixelroot32::core::Actor* actor, unsigned long deltaTime) override {\n        actor->x += speed * (deltaTime * 0.001f);\n    }\n};\n\nclass CircularMovement : public MovementStrategy {\npublic:\n    void update(pixelroot32::core::Actor* actor, unsigned long deltaTime) override {\n        float angle = millis() * 0.001f;\n        actor->x = centerX + radius * cos(angle);\n        actor->y = centerY + radius * sin(angle);\n    }\n};\n\nclass SmartEnemy : public pixelroot32::core::Actor {\nprivate:\n    MovementStrategy* movement;\n\npublic:\n    void setMovement(MovementStrategy* strat) {\n        movement = strat;\n    }\n\n    void update(unsigned long deltaTime) override {\n        if (movement) {\n            movement->update(this, deltaTime);\n        }\n    }\n};\n
    "},{"location":"manual/optimization/extensibility/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/optimization/extensibility/#driver-not-working","title":"Driver Not Working","text":"
    • Verify all interface methods are implemented
    • Check initialization order
    • Test with simple drawing first
    • Verify hardware connections
    "},{"location":"manual/optimization/extensibility/#audio-backend-issues","title":"Audio Backend Issues","text":"
    • Check sample rate matches hardware
    • Verify audio generation logic
    • Test with simple tones first
    • Check channel management
    "},{"location":"manual/optimization/extensibility/#extension-conflicts","title":"Extension Conflicts","text":"
    • Ensure namespace isolation
    • Avoid modifying engine code directly
    • Use composition over modification
    • Test with engine updates
    "},{"location":"manual/optimization/extensibility/#next-steps","title":"Next Steps","text":"

    Now that you understand extensibility, you've completed the optimization section. Continue with: - API Reference - Complete API documentation - Examples - Code examples - Resources - Tools and troubleshooting

    See also: - API Reference - DrawSurface - API Reference - AudioBackend - Manual - Platforms and Drivers

    "},{"location":"manual/optimization/memory_management/","title":"Memory Management","text":"

    ESP32 has limited memory, so efficient memory management is crucial for PixelRoot32 games. This guide covers memory constraints, object pooling, and best practices.

    "},{"location":"manual/optimization/memory_management/#esp32-memory-constraints","title":"ESP32 Memory Constraints","text":""},{"location":"manual/optimization/memory_management/#available-memory","title":"Available Memory","text":"

    ESP32 typically has: - RAM: ~320KB total (varies by model) - Flash: 4MB+ (for program storage) - Heap: Limited and fragmented over time

    "},{"location":"manual/optimization/memory_management/#real-world-limits","title":"Real-World Limits","text":"
    • MAX_ENTITIES: 32 per scene (hard limit)
    • Sprite data: Stored in flash (const/constexpr)
    • Dynamic allocation: Should be avoided in game loop
    • Stack: Limited (~8KB), avoid large stack allocations
    "},{"location":"manual/optimization/memory_management/#object-pooling","title":"Object Pooling","text":"

    Object pooling reuses objects instead of creating/destroying them, avoiding memory fragmentation.

    "},{"location":"manual/optimization/memory_management/#basic-pool-pattern","title":"Basic Pool Pattern","text":"
    class ProjectilePool {\nprivate:\n    static const int POOL_SIZE = 10;\n    ProjectileActor pool[POOL_SIZE];\n    bool inUse[POOL_SIZE];\n\npublic:\n    ProjectilePool() {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            inUse[i] = false;\n        }\n    }\n\n    ProjectileActor* getAvailable() {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (!inUse[i]) {\n                inUse[i] = true;\n                return &pool[i];\n            }\n        }\n        return nullptr; // Pool exhausted\n    }\n\n    void release(ProjectileActor* projectile) {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (&pool[i] == projectile) {\n                inUse[i] = false;\n                projectile->isEnabled = false;\n                projectile->isVisible = false;\n                break;\n            }\n        }\n    }\n};\n
    "},{"location":"manual/optimization/memory_management/#using-object-pools","title":"Using Object Pools","text":"
    class GameScene : public pixelroot32::core::Scene {\nprivate:\n    ProjectilePool projectilePool;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n\n        // Fire projectile\n        if (input.isButtonPressed(Buttons::A)) {\n            ProjectileActor* proj = projectilePool.getAvailable();\n            if (proj) {\n                proj->x = player->x;\n                proj->y = player->y;\n                proj->isEnabled = true;\n                proj->isVisible = true;\n                // ... initialize projectile\n            }\n        }\n\n        // Clean up projectiles that hit target\n        for (auto* entity : entities) {\n            if (auto* proj = dynamic_cast<ProjectileActor*>(entity)) {\n                if (proj->hitTarget) {\n                    projectilePool.release(proj);\n                }\n            }\n        }\n    }\n};\n
    "},{"location":"manual/optimization/memory_management/#complete-example-entity-pool","title":"Complete Example: Entity Pool","text":"
    template<typename T, int POOL_SIZE>\nclass EntityPool {\nprivate:\n    T pool[POOL_SIZE];\n    bool inUse[POOL_SIZE];\n    int activeCount = 0;\n\npublic:\n    EntityPool() {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            inUse[i] = false;\n        }\n    }\n\n    T* acquire() {\n        if (activeCount >= POOL_SIZE) {\n            return nullptr; // Pool full\n        }\n\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (!inUse[i]) {\n                inUse[i] = true;\n                activeCount++;\n                return &pool[i];\n            }\n        }\n        return nullptr;\n    }\n\n    void release(T* obj) {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (&pool[i] == obj) {\n                inUse[i] = false;\n                activeCount--;\n                obj->isEnabled = false;\n                obj->isVisible = false;\n                break;\n            }\n        }\n    }\n\n    int getActiveCount() const { return activeCount; }\n    int getAvailableCount() const { return POOL_SIZE - activeCount; }\n};\n\n// Usage\nEntityPool<EnemyActor, 8> enemyPool;\nEntityPool<ParticleEmitter, 5> particlePool;\n
    "},{"location":"manual/optimization/memory_management/#scene-arena-experimental","title":"Scene Arena (Experimental)","text":"

    Scene Arena provides a memory arena for scene-specific allocations, reducing fragmentation.

    "},{"location":"manual/optimization/memory_management/#what-is-scene-arena","title":"What is Scene Arena?","text":"

    Scene Arena is a contiguous memory block pre-allocated for a scene. All scene entities are allocated from this arena instead of the heap.

    "},{"location":"manual/optimization/memory_management/#when-to-use","title":"When to Use","text":"
    • Large scenes: Scenes with many entities
    • Frequent allocation: Scenes that create/destroy entities often
    • Memory fragmentation: When heap fragmentation is a problem
    • Performance: When you need predictable allocation performance
    "},{"location":"manual/optimization/memory_management/#configuration","title":"Configuration","text":"
    #ifdef PIXELROOT32_ENABLE_SCENE_ARENA\n#include <core/Scene.h>\n\n// Define arena buffer (typically in scene header)\nstatic unsigned char MY_SCENE_ARENA_BUFFER[8192]; // 8KB arena\n\nclass MyScene : public pixelroot32::core::Scene {\npublic:\n    void init() override {\n        // Initialize arena\n        arena.init(MY_SCENE_ARENA_BUFFER, sizeof(MY_SCENE_ARENA_BUFFER));\n\n        // Now entities allocated with arena will use this memory\n        // (Requires custom allocation functions)\n    }\n};\n#endif\n
    "},{"location":"manual/optimization/memory_management/#limitations","title":"Limitations","text":"
    • Experimental: May have bugs or limitations
    • Fixed size: Arena size must be determined at compile time
    • No reallocation: Can't resize arena at runtime
    • Manual management: Requires careful memory management

    Note: Scene Arena is an experimental feature. Use object pooling for most cases.

    "},{"location":"manual/optimization/memory_management/#best-practices","title":"Best Practices","text":""},{"location":"manual/optimization/memory_management/#avoid-dynamic-allocation-in-game-loop","title":"Avoid Dynamic Allocation in Game Loop","text":"
    // \u274c BAD: Allocates every frame\nvoid update(unsigned long deltaTime) override {\n    if (shouldSpawnEnemy) {\n        EnemyActor* enemy = new EnemyActor(x, y);\n        addEntity(enemy);\n    }\n}\n\n// \u2705 GOOD: Use pool\nvoid update(unsigned long deltaTime) override {\n    if (shouldSpawnEnemy) {\n        EnemyActor* enemy = enemyPool.getAvailable();\n        if (enemy) {\n            enemy->reset(x, y);\n            enemy->isEnabled = true;\n        }\n    }\n}\n
    "},{"location":"manual/optimization/memory_management/#pre-allocate-resources","title":"Pre-allocate Resources","text":"
    class GameScene : public pixelroot32::core::Scene {\nprivate:\n    // Pre-allocated pools\n    ProjectilePool projectiles;\n    EnemyPool enemies;\n    ParticlePool particles;\n\npublic:\n    void init() override {\n        // All pools created in constructor\n        // No allocation in init() or update()\n    }\n};\n
    "},{"location":"manual/optimization/memory_management/#reuse-objects","title":"Reuse Objects","text":"
    class EnemyActor : public pixelroot32::core::Actor {\npublic:\n    void reset(float x, float y) {\n        this->x = x;\n        this->y = y;\n        this->isEnabled = true;\n        this->isVisible = true;\n        this->health = maxHealth;\n        // Reset all state\n    }\n\n    void deactivate() {\n        isEnabled = false;\n        isVisible = false;\n    }\n};\n\n// Usage\nEnemyActor* enemy = enemyPool.getAvailable();\nif (enemy) {\n    enemy->reset(spawnX, spawnY);\n    addEntity(enemy);\n}\n
    "},{"location":"manual/optimization/memory_management/#avoid-strings-and-dynamic-memory","title":"Avoid Strings and Dynamic Memory","text":"
    // \u274c BAD: String allocation\nvoid draw(Renderer& renderer) override {\n    std::string scoreText = \"Score: \" + std::to_string(score);\n    renderer.drawText(scoreText.c_str(), 10, 10, Color::White, 1);\n}\n\n// \u2705 GOOD: Static buffer\nvoid draw(Renderer& renderer) override {\n    char scoreBuffer[32];\n    snprintf(scoreBuffer, sizeof(scoreBuffer), \"Score: %d\", score);\n    renderer.drawText(scoreBuffer, 10, 10, Color::White, 1);\n}\n
    "},{"location":"manual/optimization/memory_management/#store-data-in-flash","title":"Store Data in Flash","text":"
    // \u2705 GOOD: Stored in flash (const/constexpr)\nstatic const uint16_t SPRITE_DATA[] = {\n    0b00111100,\n    0b01111110,\n    // ...\n};\n\n// \u274c BAD: Stored in RAM\nuint16_t spriteData[] = {\n    0b00111100,\n    0b01111110,\n    // ...\n};\n
    "},{"location":"manual/optimization/memory_management/#memory-monitoring","title":"Memory Monitoring","text":""},{"location":"manual/optimization/memory_management/#check-available-memory","title":"Check Available Memory","text":"
    #ifdef PLATFORM_ESP32\n#include <Arduino.h>\n\nvoid checkMemory() {\n    Serial.print(\"Free heap: \");\n    Serial.println(ESP.getFreeHeap());\n    Serial.print(\"Largest free block: \");\n    Serial.println(ESP.getMaxAllocHeap());\n}\n#endif\n
    "},{"location":"manual/optimization/memory_management/#monitor-entity-count","title":"Monitor Entity Count","text":"
    void update(unsigned long deltaTime) override {\n    Scene::update(deltaTime);\n\n    // Check entity count\n    int entityCount = getEntityCount();\n    if (entityCount >= MAX_ENTITIES) {\n        Serial.println(\"WARNING: Entity limit reached!\");\n    }\n}\n
    "},{"location":"manual/optimization/memory_management/#common-patterns","title":"Common Patterns","text":""},{"location":"manual/optimization/memory_management/#entity-lifecycle-management","title":"Entity Lifecycle Management","text":"
    class ManagedEntity {\nprivate:\n    bool isActive = false;\n\npublic:\n    void activate(float x, float y) {\n        this->x = x;\n        this->y = y;\n        isActive = true;\n        isEnabled = true;\n        isVisible = true;\n    }\n\n    void deactivate() {\n        isActive = false;\n        isEnabled = false;\n        isVisible = false;\n    }\n\n    bool getIsActive() const { return isActive; }\n};\n\n// Pool manages lifecycle\nclass EntityManager {\nprivate:\n    EntityPool<ManagedEntity, 20> pool;\n\npublic:\n    ManagedEntity* spawn(float x, float y) {\n        auto* entity = pool.acquire();\n        if (entity) {\n            entity->activate(x, y);\n        }\n        return entity;\n    }\n\n    void despawn(ManagedEntity* entity) {\n        if (entity) {\n            entity->deactivate();\n            pool.release(entity);\n        }\n    }\n};\n
    "},{"location":"manual/optimization/memory_management/#memory-efficient-collections","title":"Memory-Efficient Collections","text":"
    // Fixed-size array instead of vector\nclass EntityArray {\nprivate:\n    static const int MAX_SIZE = 32;\n    pixelroot32::core::Entity* entities[MAX_SIZE];\n    int count = 0;\n\npublic:\n    bool add(pixelroot32::core::Entity* entity) {\n        if (count >= MAX_SIZE) return false;\n        entities[count++] = entity;\n        return true;\n    }\n\n    void remove(pixelroot32::core::Entity* entity) {\n        for (int i = 0; i < count; i++) {\n            if (entities[i] == entity) {\n                entities[i] = entities[--count];\n                break;\n            }\n        }\n    }\n\n    int size() const { return count; }\n    pixelroot32::core::Entity* operator[](int index) { return entities[index]; }\n};\n
    "},{"location":"manual/optimization/memory_management/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/optimization/memory_management/#out-of-memory-errors","title":"Out of Memory Errors","text":"
    • Reduce pool sizes
    • Use fewer entities
    • Store more data in flash
    • Avoid dynamic allocation
    • Check for memory leaks
    "},{"location":"manual/optimization/memory_management/#entity-limit-reached","title":"Entity Limit Reached","text":"
    • MAX_ENTITIES = 32 is a hard limit
    • Use object pooling to reuse entities
    • Deactivate entities instead of removing
    • Combine multiple entities into one
    "},{"location":"manual/optimization/memory_management/#memory-fragmentation","title":"Memory Fragmentation","text":"
    • Use object pooling
    • Pre-allocate all resources
    • Avoid frequent new/delete
    • Consider Scene Arena (experimental)
    "},{"location":"manual/optimization/memory_management/#next-steps","title":"Next Steps","text":"

    Now that you understand memory management, learn about: - Performance Optimization - Improve game performance - Platforms and Drivers - Understand platform specifics - Extensibility - Extend the engine

    See also: - API Reference - Scene - Manual - Scenes and Entities

    "},{"location":"manual/optimization/performance_tuning/","title":"Performance Optimization","text":"

    This guide covers techniques to improve game performance on ESP32, including rendering optimization, logic optimization, and profiling.

    "},{"location":"manual/optimization/performance_tuning/#esp32-performance-characteristics","title":"ESP32 Performance Characteristics","text":""},{"location":"manual/optimization/performance_tuning/#cpu-limitations","title":"CPU Limitations","text":"
    • Dual-core: 240MHz (typically)
    • Single-threaded game loop: One core handles everything
    • Target FPS: 30-60 FPS (depends on game complexity)
    • Frame budget: ~16-33ms per frame at 60 FPS
    "},{"location":"manual/optimization/performance_tuning/#common-bottlenecks","title":"Common Bottlenecks","text":"
    1. Rendering: Too many draw calls
    2. Collision detection: Too many collision checks
    3. Memory allocation: Dynamic allocation in game loop
    4. Complex calculations: Expensive math operations
    5. String operations: String concatenation/formatting
    "},{"location":"manual/optimization/performance_tuning/#tecnicas-de-optimizacion","title":"T\u00e9cnicas de Optimizaci\u00f3n","text":"

    El motor utiliza varias t\u00e9cnicas para maximizar los FPS, especialmente en hardware limitado como el ESP32.

    "},{"location":"manual/optimization/performance_tuning/#1-viewport-culling-recorte-de-camara","title":"1. Viewport Culling (Recorte de C\u00e1mara)","text":"

    No proceses objetos que est\u00e1n fuera de la pantalla. El motor lo hace autom\u00e1ticamente en drawTileMap, pero debes implementarlo en tu l\u00f3gica de actualizaci\u00f3n:

    bool isOnScreen(float x, float y, int width, int height, \n                const Camera2D& camera) {\n    float cameraX = camera.getX();\n    float cameraY = camera.getY();\n    int screenWidth = engine.getRenderer().getWidth();\n    int screenHeight = engine.getRenderer().getHeight();\n\n    return !(x + width < cameraX || \n             x > cameraX + screenWidth ||\n             y + height < cameraY || \n             y > cameraY + screenHeight);\n}\n
    "},{"location":"manual/optimization/performance_tuning/#2-optimizacion-de-memoria-y-cpu-esp32","title":"2. Optimizaci\u00f3n de Memoria y CPU (ESP32)","text":"

    Para la plataforma ESP32, se han implementado optimizaciones de bajo nivel cr\u00edticas:

    • IRAM_ATTR: Las funciones cr\u00edticas de renderizado (drawSprite, drawTileMap, etc.) est\u00e1n marcadas para ejecutarse desde la RAM interna (IRAM), eliminando la latencia de lectura de la Flash SPI.
    • DMA (Direct Memory Access): El volcado del buffer a la pantalla TFT se realiza mediante DMA, lo que permite que la CPU comience a procesar el siguiente frame mientras el hardware transfiere los datos.
    • Acceso a Datos de 16 bits: Los sprites de 2bpp y 4bpp utilizan punteros uint16_t* para garantizar accesos alineados a memoria, lo cual es significativamente m\u00e1s r\u00e1pido en la arquitectura Xtensa del ESP32.
    "},{"location":"manual/optimization/performance_tuning/#3-optimizacion-de-tilemaps","title":"3. Optimizaci\u00f3n de TileMaps","text":"

    El renderizado de mapas de tiles es una de las operaciones m\u00e1s costosas. PixelRoot32 utiliza:

    • Cach\u00e9 de Paleta: Durante el dibujado de un tilemap, se genera una tabla de b\u00fasqueda (LUT) temporal para evitar c\u00e1lculos de color redundantes por cada p\u00edxel.
    • Dibujado por Columnas: Optimizado para minimizar los saltos de memoria en el framebuffer.
    "},{"location":"manual/optimization/performance_tuning/#4-colisiones-eficientes","title":"4. Colisiones Eficientes","text":"

    Usa colisiones basadas en tiles siempre que sea posible. Acceder a un array de tiles es O(1), mientras que iterar sobre una lista de entidades es O(n).

    // Ejemplo de colisi\u00f3n r\u00e1pida con el mapa\nint tileX = x / 8;\nint tileY = y / 8;\nif (levelMap.data[tileY * levelMap.width + tileX] != 0) {\n    // Colisi\u00f3n detectada\n}\n
    "},{"location":"manual/optimization/performance_tuning/#recomendaciones-generales","title":"Recomendaciones Generales","text":"
    • Sprites Indexados: Prefiere Sprite2bpp (4 colores) o Sprite4bpp (16 colores) sobre Sprite (1bpp) si necesitas color, ya que est\u00e1n altamente optimizados.
    • Evitar std::string en el Loop: Las concatenaciones de strings generan fragmentaci\u00f3n de memoria. Usa buffers est\u00e1ticos o char[] para textos din\u00e1micos.
    • Perfilado: Activa el overlay de FPS compilando con PIXELROOT32_ENABLE_FPS_DISPLAY (ver Engine - FPS overlay) para monitorear el impacto de tus cambios en tiempo real.
    "},{"location":"manual/optimization/performance_tuning/#common-optimization-patterns","title":"Common Optimization Patterns","text":""},{"location":"manual/optimization/performance_tuning/#update-frequency-reduction","title":"Update Frequency Reduction","text":"
    class LowFrequencyUpdater {\nprivate:\n    unsigned long timer = 0;\n    unsigned long interval = 100; // Update every 100ms\n\npublic:\n    void update(unsigned long deltaTime) {\n        timer += deltaTime;\n        if (timer >= interval) {\n            timer -= interval;\n            // Do expensive update\n            expensiveUpdate();\n        }\n    }\n};\n
    "},{"location":"manual/optimization/performance_tuning/#spatial-partitioning-simple","title":"Spatial Partitioning (Simple)","text":"
    // Divide screen into zones\nclass SpatialGrid {\nprivate:\n    static const int GRID_SIZE = 4;\n    static const int CELL_WIDTH = 60;\n    static const int CELL_HEIGHT = 60;\n\n    std::vector<Actor*> grid[GRID_SIZE][GRID_SIZE];\n\npublic:\n    void add(Actor* actor) {\n        int cellX = static_cast<int>(actor->x) / CELL_WIDTH;\n        int cellY = static_cast<int>(actor->y) / CELL_HEIGHT;\n        if (cellX >= 0 && cellX < GRID_SIZE && \n            cellY >= 0 && cellY < GRID_SIZE) {\n            grid[cellY][cellX].push_back(actor);\n        }\n    }\n\n    void checkCollisions() {\n        // Only check collisions within same cell\n        for (int y = 0; y < GRID_SIZE; y++) {\n            for (int x = 0; x < GRID_SIZE; x++) {\n                auto& cell = grid[y][x];\n                for (size_t i = 0; i < cell.size(); i++) {\n                    for (size_t j = i + 1; j < cell.size(); j++) {\n                        checkCollision(cell[i], cell[j]);\n                    }\n                }\n            }\n        }\n    }\n};\n
    "},{"location":"manual/optimization/performance_tuning/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/optimization/performance_tuning/#low-fps","title":"Low FPS","text":"
    • Profile to find bottlenecks
    • Reduce entity count
    • Optimize rendering (culling, batching)
    • Simplify collision detection
    • Reduce update frequency
    "},{"location":"manual/optimization/performance_tuning/#frame-drops","title":"Frame Drops","text":"
    • Check for expensive operations in update()
    • Avoid dynamic allocation
    • Cache calculations
    • Reduce draw calls
    "},{"location":"manual/optimization/performance_tuning/#stuttering","title":"Stuttering","text":"
    • Ensure frame-rate independence (use deltaTime)
    • Avoid blocking operations
    • Pre-load resources
    • Use object pooling
    "},{"location":"manual/optimization/performance_tuning/#next-steps","title":"Next Steps","text":"

    Now that you understand performance optimization, learn about: - Memory Management - Manage memory efficiently - Platforms and Drivers - Platform-specific optimizations - Extensibility - Extend the engine

    See also: - Manual - Basic Rendering - Manual - Physics and Collisions

    "},{"location":"manual/optimization/platforms_and_drivers/","title":"Platforms and Drivers","text":"

    PixelRoot32 supports multiple platforms through driver abstraction. This guide covers supported platforms, display drivers, audio backends, and build configuration.

    "},{"location":"manual/optimization/platforms_and_drivers/#supported-platforms","title":"Supported Platforms","text":""},{"location":"manual/optimization/platforms_and_drivers/#esp32","title":"ESP32","text":"

    Primary platform for PixelRoot32 games.

    Characteristics: - TFT_eSPI display driver - Internal DAC or I2S audio - GPIO button input - Limited RAM/Flash - Real hardware constraints

    Use for: - Final game deployment - Hardware testing - Production builds

    "},{"location":"manual/optimization/platforms_and_drivers/#nativedesktop-sdl2","title":"Native/Desktop (SDL2)","text":"

    Development platform for rapid iteration.

    Characteristics: - SDL2 display driver - SDL2 audio backend - Keyboard input - Unlimited resources (for testing) - Fast development cycle

    Use for: - Development and debugging - Testing without hardware - Rapid prototyping - CI/CD testing

    "},{"location":"manual/optimization/platforms_and_drivers/#display-drivers","title":"Display Drivers","text":""},{"location":"manual/optimization/platforms_and_drivers/#tft_espi-esp32","title":"TFT_eSPI (ESP32)","text":"

    TFT_eSPI is the display driver for ESP32, supporting many TFT displays.

    "},{"location":"manual/optimization/platforms_and_drivers/#optimizaciones-esp32","title":"Optimizaciones ESP32","text":"

    Para maximizar el rendimiento en ESP32, PixelRoot32 utiliza:

    • DMA (Direct Memory Access): Las transferencias al display se realizan en segundo plano, permitiendo que la CPU prepare el siguiente frame mientras se env\u00eda el actual.
    • Doble Buffer con IRAM: El motor utiliza un buffer de pantalla (Sprite de TFT_eSPI) optimizado para transferencias r\u00e1pidas.
    • Alineaci\u00f3n de 16 bits: Los datos de sprites 2bpp/4bpp est\u00e1n alineados a palabras de 16 bits para aprovechar la arquitectura Xtensa.
    • IRAM_ATTR: Las funciones cr\u00edticas de renderizado est\u00e1n marcadas para residir en la RAM de instrucciones, evitando cuellos de botella por acceso a la Flash.
    "},{"location":"manual/optimization/platforms_and_drivers/#configuracion-dma","title":"Configuraci\u00f3n DMA","text":"

    El DMA se activa autom\u00e1ticamente si el hardware lo soporta. Aseg\u00farate de configurar la frecuencia SPI adecuada para tu display (usualmente 40MHz u 80MHz).

    [env:esp32dev]\nbuild_flags = \n    -D ST7789_DRIVER          # Display type\n    -D TFT_WIDTH=240          # Display width\n    -D TFT_HEIGHT=240         # Display height\n    -D TFT_MOSI=23            # SPI MOSI pin\n    -D TFT_SCLK=18            # SPI clock pin\n    -D TFT_DC=2               # Data/Command pin\n    -D TFT_RST=4              # Reset pin\n    -D TFT_CS=-1              # Chip select (-1 if not used)\n    -D SPI_FREQUENCY=40000000 # SPI frequency\n
    "},{"location":"manual/optimization/platforms_and_drivers/#supported-displays","title":"Supported Displays","text":"
    • ST7735: 128x128, 128x160
    • ST7789: 240x240, 240x320
    • ILI9341: 240x320
    • And more: See TFT_eSPI documentation
    "},{"location":"manual/optimization/platforms_and_drivers/#usage","title":"Usage","text":"
    #include <drivers/esp32/TFT_eSPI_Drawer.h>\n\n// Display configuration\npixelroot32::graphics::DisplayConfig displayConfig(\n    pixelroot32::graphics::DisplayType::ST7789,\n    0,      // rotation\n    240,    // width\n    240     // height\n);\n\n// TFT_eSPI_Drawer is created automatically by Engine\n// No manual driver creation needed\n
    "},{"location":"manual/optimization/platforms_and_drivers/#sdl2_drawer-native","title":"SDL2_Drawer (Native)","text":"

    SDL2_Drawer provides display output for PC/desktop development.

    "},{"location":"manual/optimization/platforms_and_drivers/#configuration","title":"Configuration","text":"
    #include <drivers/native/SDL2_Drawer.h>\n\n// Display configuration (NONE defaults to SDL2)\npixelroot32::graphics::DisplayConfig displayConfig(\n    pixelroot32::graphics::DisplayType::NONE,\n    0,      // rotation\n    240,    // width\n    240     // height\n);\n\n// SDL2_Drawer is created automatically\n
    "},{"location":"manual/optimization/platforms_and_drivers/#sdl2-installation","title":"SDL2 Installation","text":"

    Windows (MSYS2):

    pacman -S mingw-w64-x86_64-SDL2\n

    Linux:

    sudo apt-get install libsdl2-dev\n

    macOS:

    brew install sdl2\n

    "},{"location":"manual/optimization/platforms_and_drivers/#audio-backends","title":"Audio Backends","text":""},{"location":"manual/optimization/platforms_and_drivers/#esp32_dac_audiobackend","title":"ESP32_DAC_AudioBackend","text":"

    Uses ESP32's internal DAC for audio output.

    "},{"location":"manual/optimization/platforms_and_drivers/#configuration_1","title":"Configuration","text":"
    #include <drivers/esp32/ESP32_DAC_AudioBackend.h>\n\nconst int DAC_PIN = 25; // GPIO 25 or 26\npixelroot32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(\n    DAC_PIN,    // DAC pin (25 or 26)\n    11025       // Sample rate (Hz)\n);\n\npixelroot32::audio::AudioConfig audioConfig(\n    &audioBackend, \n    audioBackend.getSampleRate()\n);\n

    Characteristics: - Simple setup (just one pin) - Lower quality than I2S - Good for basic audio - Sample rate: 11025 Hz recommended

    "},{"location":"manual/optimization/platforms_and_drivers/#esp32_i2s_audiobackend","title":"ESP32_I2S_AudioBackend","text":"

    Uses ESP32's I2S peripheral for higher quality audio.

    "},{"location":"manual/optimization/platforms_and_drivers/#configuration_2","title":"Configuration","text":"
    #include <drivers/esp32/ESP32_I2S_AudioBackend.h>\n\nconst int I2S_BCLK = 26;  // Bit clock pin\nconst int I2S_LRCK = 25;  // Left/Right clock pin\nconst int I2S_DOUT = 22;  // Data out pin\n\npixelroot32::drivers::esp32::ESP32_I2S_AudioBackend audioBackend(\n    I2S_BCLK,\n    I2S_LRCK,\n    I2S_DOUT,\n    22050  // Sample rate (Hz)\n);\n\npixelroot32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n

    Characteristics: - Higher quality than DAC - Requires external I2S DAC (e.g., MAX98357A) - Better for music - Sample rate: 22050 Hz recommended

    "},{"location":"manual/optimization/platforms_and_drivers/#sdl2_audiobackend-native","title":"SDL2_AudioBackend (Native)","text":"

    SDL2 audio for PC development.

    "},{"location":"manual/optimization/platforms_and_drivers/#configuration_3","title":"Configuration","text":"
    #include <drivers/native/SDL2_AudioBackend.h>\n\npixelroot32::drivers::native::SDL2_AudioBackend audioBackend(\n    22050,  // Sample rate\n    1024    // Buffer size\n);\n\npixelroot32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n
    "},{"location":"manual/optimization/platforms_and_drivers/#build-flags","title":"Build Flags","text":""},{"location":"manual/optimization/platforms_and_drivers/#experimental-features","title":"Experimental Features","text":"

    Enable experimental features with build flags:

    [env:esp32dev]\nbuild_flags = \n    -D PIXELROOT32_ENABLE_2BPP_SPRITES    # Enable 2bpp sprite format\n    -D PIXELROOT32_ENABLE_4BPP_SPRITES   # Enable 4bpp sprite format\n    -D PIXELROOT32_ENABLE_SCENE_ARENA    # Enable Scene Arena (experimental)\n    -D PIXELROOT32_ENABLE_FPS_DISPLAY    # On-screen FPS counter (green, top-right; value updated every 8 frames)\n
    "},{"location":"manual/optimization/platforms_and_drivers/#fps-overlay-pixelroot32_enable_fps_display","title":"FPS overlay (PIXELROOT32_ENABLE_FPS_DISPLAY)","text":"

    When defined, the engine draws an on-screen FPS counter (green text, top-right) each frame. The value is recalculated every 8 frames to keep per-frame cost low. No code changes are required; the overlay is drawn automatically after the scene. See API Reference - Engine - Optional: FPS overlay for details.

    "},{"location":"manual/optimization/platforms_and_drivers/#scene-limits-max_layers-max_entities","title":"Scene limits (MAX_LAYERS / MAX_ENTITIES)","text":"

    You can override the default scene limits from your project without modifying the engine. The default of 3 for MAX_LAYERS is due to ESP32 platform constraints (memory and draw-loop cost); on native/PC you can use a higher value.

    Option A: Compiler flags (recommended) \u2014 in platformio.ini, add to build_flags for your environment:

    build_flags =\n    -DMAX_LAYERS=5\n    -DMAX_ENTITIES=64\n

    The compiler defines these before any .cpp is processed. Because Scene.h uses #ifndef MAX_LAYERS / #ifndef MAX_ENTITIES, your values are used (more render layers drawn in Scene::draw, and on Arduino the entity queue capacity when built with MAX_ENTITIES).

    See API Reference - Scene - Overriding scene limits for details.

    "},{"location":"manual/optimization/platforms_and_drivers/#platform-detection","title":"Platform Detection","text":"
    #ifdef PLATFORM_ESP32\n    // ESP32-specific code\n    Serial.println(\"Running on ESP32\");\n#endif\n\n#ifdef PLATFORM_NATIVE\n    // Native/PC-specific code\n    printf(\"Running on PC\\n\");\n#endif\n
    "},{"location":"manual/optimization/platforms_and_drivers/#optimization-flags","title":"Optimization Flags","text":"
    [env:esp32dev]\nbuild_flags = \n    -O2              # Optimization level\n    -ffunction-sections\n    -fdata-sections\n    -Wl,--gc-sections\n
    "},{"location":"manual/optimization/platforms_and_drivers/#complete-platform-setup-examples","title":"Complete Platform Setup Examples","text":""},{"location":"manual/optimization/platforms_and_drivers/#esp32-complete-setup","title":"ESP32 Complete Setup","text":"
    #include <Arduino.h>\n#include <core/Engine.h>\n#include <drivers/esp32/TFT_eSPI_Drawer.h>\n#include <drivers/esp32/ESP32_DAC_AudioBackend.h>\n\nnamespace pr32 = pixelroot32;\n\n// Audio\nconst int DAC_PIN = 25;\npr32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(DAC_PIN, 11025);\n\n// Display\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::ST7789,\n    0, 240, 240\n);\n\n// Input\npr32::input::InputConfig inputConfig(\n    6, 32, 27, 33, 14, 13, 12  // 6 buttons, pins\n);\n\n// Audio config\npr32::audio::AudioConfig audioConfig(&audioBackend, 11025);\n\n// Engine\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\nvoid setup() {\n    Serial.begin(115200);\n    engine.init();\n    // ... scene setup\n}\n\nvoid loop() {\n    engine.run();\n}\n
    "},{"location":"manual/optimization/platforms_and_drivers/#native-complete-setup","title":"Native Complete Setup","text":"
    #define SDL_MAIN_HANDLED\n#include <SDL2/SDL.h>\n#include <core/Engine.h>\n#include <drivers/native/SDL2_Drawer.h>\n#include <drivers/native/SDL2_AudioBackend.h>\n\nnamespace pr32 = pixelroot32;\n\n// Audio\npr32::drivers::native::SDL2_AudioBackend audioBackend(22050, 1024);\n\n// Display\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::NONE,\n    0, 240, 240\n);\n\n// Input (SDL scancodes)\npr32::input::InputConfig inputConfig(\n    6,\n    SDL_SCANCODE_UP,\n    SDL_SCANCODE_DOWN,\n    SDL_SCANCODE_LEFT,\n    SDL_SCANCODE_RIGHT,\n    SDL_SCANCODE_SPACE,\n    SDL_SCANCODE_RETURN\n);\n\n// Audio config\npr32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n\n// Engine\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\nint main(int argc, char* argv[]) {\n    engine.init();\n    // ... scene setup\n    engine.run();\n    return 0;\n}\n
    "},{"location":"manual/optimization/platforms_and_drivers/#platform-specific-considerations","title":"Platform-Specific Considerations","text":""},{"location":"manual/optimization/platforms_and_drivers/#esp32_1","title":"ESP32","text":"

    Memory: - Limited RAM (~320KB) - Use object pooling - Store data in flash - Avoid dynamic allocation

    Performance: - Target 30-60 FPS - Optimize rendering - Reduce entity count - Profile on hardware

    Hardware: - GPIO pin configuration - SPI display setup - Audio hardware connections - Power considerations

    "},{"location":"manual/optimization/platforms_and_drivers/#native","title":"Native","text":"

    Development: - Fast iteration - Easy debugging - Unlimited resources - Visual debugging tools

    Testing: - Test logic without hardware - Rapid prototyping - CI/CD integration - Cross-platform testing

    "},{"location":"manual/optimization/platforms_and_drivers/#troubleshooting","title":"Troubleshooting","text":""},{"location":"manual/optimization/platforms_and_drivers/#esp32-display-issues","title":"ESP32 Display Issues","text":"
    • Check wiring connections
    • Verify pin numbers
    • Lower SPI frequency
    • Check display type matches
    • Verify power supply
    "},{"location":"manual/optimization/platforms_and_drivers/#esp32-audio-issues","title":"ESP32 Audio Issues","text":"
    • Check DAC/I2S pin configuration
    • Verify sample rate
    • Check hardware connections
    • Lower volume if distorted
    • Test with different sample rates
    "},{"location":"manual/optimization/platforms_and_drivers/#native-build-issues","title":"Native Build Issues","text":"
    • Verify SDL2 installation
    • Check include/library paths
    • Ensure SDL2 version compatibility
    • Check linker flags
    "},{"location":"manual/optimization/platforms_and_drivers/#next-steps","title":"Next Steps","text":"

    Now that you understand platforms and drivers, learn about: - Extensibility - Create custom drivers - Memory Management - ESP32 memory constraints - Performance Optimization - Platform-specific optimization

    See also: - API Reference - DrawSurface - API Reference - AudioBackend - Getting Started - Your First Project

    "},{"location":"reference/api_overview/","title":"API Reference Overview","text":"

    This document provides a complete technical reference for all PixelRoot32 APIs, organized by module. Each class includes descriptions, constructors, methods, properties, and usage examples.

    "},{"location":"reference/api_overview/#organization","title":"Organization","text":"

    The API is organized into the following modules:

    • Core: Engine, Scene, Entity, Actor, PhysicsActor, SceneManager
    • Graphics: Renderer, Camera2D, Color, Font, Sprite, TileMap, DrawSurface
    • Audio: AudioEngine, MusicPlayer, AudioTypes, AudioConfig, AudioBackend
    • Input: InputManager, InputConfig
    • Physics: CollisionSystem, CollisionTypes
    • UI: UIElement, UIButton, UILabel, UILayouts
    • Particles: ParticleEmitter, ParticleConfig, ParticlePresets
    "},{"location":"reference/api_overview/#quick-navigation","title":"Quick Navigation","text":""},{"location":"reference/api_overview/#core-module","title":"Core Module","text":"
    • Engine - Main engine class, game loop management
    • Scene - Scene/level management
    • Entity - Base game object class
    • Actor - Entity with collision support
    • PhysicsActor - Actor with automatic physics
    • InputManager - Input handling
    • InputConfig - Input configuration
    "},{"location":"reference/api_overview/#graphics-module","title":"Graphics Module","text":"
    • Renderer - High-level rendering API
    • Camera2D - 2D camera for scrolling
    • Color - Color constants and utilities
    • Font - Bitmap font system
    • Sprite - Sprite structures and formats
    • TileMap - Tilemap structure
    • DisplayConfig - Display configuration
    "},{"location":"reference/api_overview/#audio-module","title":"Audio Module","text":"
    • AudioEngine - Sound effects playback
    • MusicPlayer - Background music playback
    • AudioTypes - Audio data structures
    • AudioConfig - Audio configuration
    "},{"location":"reference/api_overview/#physics-module","title":"Physics Module","text":"
    • CollisionSystem - Collision detection
    • CollisionTypes - Collision primitives
    "},{"location":"reference/api_overview/#ui-module","title":"UI Module","text":"
    • UIElement - Base UI element class
    • UIButton - Clickable button
    • UILabel - Text label
    • UILayouts - Layout containers
    "},{"location":"reference/api_overview/#api-documentation-format","title":"API Documentation Format","text":"

    Each API reference page follows this structure:

    "},{"location":"reference/api_overview/#class-name","title":"Class Name","text":"

    Brief description of the class and its purpose.

    "},{"location":"reference/api_overview/#namespace","title":"Namespace","text":"
    namespace pixelroot32::module {\n    class ClassName {\n        // ...\n    };\n}\n
    "},{"location":"reference/api_overview/#constructors","title":"Constructors","text":"

    List of all constructors with parameters.

    "},{"location":"reference/api_overview/#public-methods","title":"Public Methods","text":"Method Description Parameters Returns methodName() Description param: type return type"},{"location":"reference/api_overview/#properties","title":"Properties","text":"Property Type Description property type Description"},{"location":"reference/api_overview/#usage-example","title":"Usage Example","text":"
    // Example code showing typical usage\n
    "},{"location":"reference/api_overview/#performance-notes","title":"Performance Notes","text":"

    Any performance considerations or limitations.

    "},{"location":"reference/api_overview/#see-also","title":"See Also","text":"

    Links to related APIs and documentation.

    "},{"location":"reference/api_overview/#finding-apis","title":"Finding APIs","text":""},{"location":"reference/api_overview/#by-functionality","title":"By Functionality","text":"
    • Game Loop: See Engine
    • Rendering: See Renderer
    • Input: See InputManager
    • Audio: See AudioEngine and MusicPlayer
    • Physics: See PhysicsActor and CollisionSystem
    • UI: See UIElement and layouts
    "},{"location":"reference/api_overview/#by-module","title":"By Module","text":"

    Navigate to the specific module folder: - api_reference/core/ - Core engine classes - api_reference/graphics/ - Rendering and graphics - api_reference/audio/ - Audio system - api_reference/physics/ - Physics and collisions - api_reference/ui/ - User interface

    "},{"location":"reference/api_overview/#complete-api-list","title":"Complete API List","text":""},{"location":"reference/api_overview/#core","title":"Core","text":"
    • Engine
    • Scene
    • Entity
    • Actor
    • PhysicsActor
    • InputManager
    • InputConfig
    "},{"location":"reference/api_overview/#graphics","title":"Graphics","text":"
    • Renderer
    • Camera2D
    • Color
    • Font
    • Sprite
    • TileMap
    • DisplayConfig
    "},{"location":"reference/api_overview/#audio","title":"Audio","text":"
    • AudioEngine
    • MusicPlayer
    • AudioTypes
    • AudioConfig
    "},{"location":"reference/api_overview/#physics","title":"Physics","text":"
    • CollisionSystem
    • CollisionTypes
    "},{"location":"reference/api_overview/#ui","title":"UI","text":"
    • UIElement
    • UIButton
    • UILabel
    • UIVerticalLayout
    • UIHorizontalLayout
    • UIGridLayout
    • UIAnchorLayout
    • UIPanel
    • UIPaddingContainer
    "},{"location":"reference/api_overview/#related-documentation","title":"Related Documentation","text":"
    • Manual - Game Development - How to use the APIs
    • Manual - Advanced Graphics - Advanced techniques
    • Code Examples - Reusable code snippets
    • Game Examples Guide - Learn from complete games

    Note: This is an overview. For detailed API documentation, see the individual reference pages linked above.

    "},{"location":"reference/code_examples/","title":"Code Examples","text":"

    A library of reusable code snippets for common PixelRoot32 tasks. All examples are complete and functional.

    "},{"location":"reference/code_examples/#initialization","title":"Initialization","text":""},{"location":"reference/code_examples/#basic-engine-setup-esp32","title":"Basic Engine Setup (ESP32)","text":"
    #include <Arduino.h>\n#include <core/Engine.h>\n#include <drivers/esp32/TFT_eSPI_Drawer.h>\n#include <drivers/esp32/ESP32_DAC_AudioBackend.h>\n\nnamespace pr32 = pixelroot32;\n\n// Audio\nconst int DAC_PIN = 25;\npr32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(DAC_PIN, 11025);\n\n// Display\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::ST7789,\n    0, 240, 240\n);\n\n// Input\npr32::input::InputConfig inputConfig(6, 32, 27, 33, 14, 13, 12);\n\n// Audio config\npr32::audio::AudioConfig audioConfig(&audioBackend, 11025);\n\n// Engine\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\nvoid setup() {\n    Serial.begin(115200);\n    engine.init();\n    // ... scene setup\n}\n\nvoid loop() {\n    engine.run();\n}\n
    "},{"location":"reference/code_examples/#basic-engine-setup-native","title":"Basic Engine Setup (Native)","text":"
    #define SDL_MAIN_HANDLED\n#include <SDL2/SDL.h>\n#include <core/Engine.h>\n#include <drivers/native/SDL2_Drawer.h>\n#include <drivers/native/SDL2_AudioBackend.h>\n\nnamespace pr32 = pixelroot32;\n\npr32::drivers::native::SDL2_AudioBackend audioBackend(22050, 1024);\npr32::graphics::DisplayConfig displayConfig(\n    pr32::graphics::DisplayType::NONE, 0, 240, 240\n);\npr32::input::InputConfig inputConfig(\n    6, SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, \n    SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT,\n    SDL_SCANCODE_SPACE, SDL_SCANCODE_RETURN\n);\npr32::audio::AudioConfig audioConfig(&audioBackend, 22050);\n\npr32::core::Engine engine(displayConfig, inputConfig, audioConfig);\n\nint main(int argc, char* argv[]) {\n    engine.init();\n    // ... scene setup\n    engine.run();\n    return 0;\n}\n
    "},{"location":"reference/code_examples/#entity-movement","title":"Entity Movement","text":""},{"location":"reference/code_examples/#simple-movement","title":"Simple Movement","text":"
    class MovingEntity : public pixelroot32::core::Entity {\nprivate:\n    float speedX = 50.0f;\n    float speedY = 30.0f;\n\npublic:\n    MovingEntity(float x, float y)\n        : Entity(x, y, 16, 16, pixelroot32::core::EntityType::GENERIC) {\n        setRenderLayer(1);\n    }\n\n    void update(unsigned long deltaTime) override {\n        float dt = deltaTime * 0.001f;\n        x += speedX * dt;\n        y += speedY * dt;\n\n        // Bounce off edges\n        if (x < 0 || x > 224) speedX = -speedX;\n        if (y < 0 || y > 224) speedY = -speedY;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x),\n            static_cast<int>(y),\n            width, height,\n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n};\n
    "},{"location":"reference/code_examples/#input-based-movement","title":"Input-Based Movement","text":"
    class PlayerEntity : public pixelroot32::core::Actor {\nprivate:\n    float speed = 100.0f;\n\npublic:\n    PlayerEntity(float x, float y)\n        : Actor(x, y, 16, 16) {\n        setRenderLayer(1);\n    }\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        float dt = deltaTime * 0.001f;\n\n        if (input.isButtonDown(Buttons::LEFT)) {\n            x -= speed * dt;\n        }\n        if (input.isButtonDown(Buttons::RIGHT)) {\n            x += speed * dt;\n        }\n        if (input.isButtonDown(Buttons::UP)) {\n            y -= speed * dt;\n        }\n        if (input.isButtonDown(Buttons::DOWN)) {\n            y += speed * dt;\n        }\n\n        // Keep on screen\n        if (x < 0) x = 0;\n        if (x > 224) x = 224;\n        if (y < 0) y = 0;\n        if (y > 224) y = 224;\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        renderer.drawFilledRectangle(\n            static_cast<int>(x),\n            static_cast<int>(y),\n            width, height,\n            pixelroot32::graphics::Color::White\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        // Handle collision\n    }\n};\n
    "},{"location":"reference/code_examples/#collisions","title":"Collisions","text":""},{"location":"reference/code_examples/#basic-collision-detection","title":"Basic Collision Detection","text":"
    class CollidableEntity : public pixelroot32::core::Actor {\npublic:\n    CollidableEntity(float x, float y)\n        : Actor(x, y, 16, 16) {\n        setRenderLayer(1);\n        setCollisionLayer(Layers::PLAYER);\n        setCollisionMask(Layers::ENEMY | Layers::WALL);\n    }\n\n    void onCollision(pixelroot32::core::Actor* other) override {\n        if (other->isInLayer(Layers::ENEMY)) {\n            // Hit enemy\n            takeDamage();\n        } else if (other->isInLayer(Layers::WALL)) {\n            // Hit wall\n            stopMovement();\n        }\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n};\n
    "},{"location":"reference/code_examples/#collision-layers-setup","title":"Collision Layers Setup","text":"
    // Define in GameLayers.h\nnamespace Layers {\n    constexpr uint16_t PLAYER = 0x0001;\n    constexpr uint16_t ENEMY = 0x0002;\n    constexpr uint16_t PROJECTILE = 0x0004;\n    constexpr uint16_t WALL = 0x0008;\n    constexpr uint16_t PICKUP = 0x0010;\n}\n\n// Usage\nplayer->setCollisionLayer(Layers::PLAYER);\nplayer->setCollisionMask(Layers::ENEMY | Layers::WALL);\n\nenemy->setCollisionLayer(Layers::ENEMY);\nenemy->setCollisionMask(Layers::PLAYER | Layers::PROJECTILE);\n
    "},{"location":"reference/code_examples/#sound-effects","title":"Sound Effects","text":""},{"location":"reference/code_examples/#common-sound-effects","title":"Common Sound Effects","text":"
    namespace SoundEffects {\n    inline pixelroot32::audio::AudioEvent jump() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::PULSE;\n        evt.frequency = 600.0f;\n        evt.duration = 0.1f;\n        evt.volume = 0.7f;\n        evt.duty = 0.25f;\n        return evt;\n    }\n\n    inline pixelroot32::audio::AudioEvent coin() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::PULSE;\n        evt.frequency = 1500.0f;\n        evt.duration = 0.12f;\n        evt.volume = 0.8f;\n        evt.duty = 0.5f;\n        return evt;\n    }\n\n    inline pixelroot32::audio::AudioEvent explosion() {\n        pixelroot32::audio::AudioEvent evt{};\n        evt.type = pixelroot32::audio::WaveType::NOISE;\n        evt.frequency = 200.0f;\n        evt.duration = 0.3f;\n        evt.volume = 0.9f;\n        return evt;\n    }\n}\n\n// Usage\nengine.getAudioEngine().playEvent(SoundEffects::jump());\n
    "},{"location":"reference/code_examples/#playing-sound-on-event","title":"Playing Sound on Event","text":"
    class PlayerActor : public pixelroot32::core::Actor {\npublic:\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n\n        if (input.isButtonPressed(Buttons::A)) {\n            // Play jump sound\n            pixelroot32::audio::AudioEvent jumpSound{};\n            jumpSound.type = pixelroot32::audio::WaveType::PULSE;\n            jumpSound.frequency = 800.0f;\n            jumpSound.duration = 0.1f;\n            jumpSound.volume = 0.7f;\n            jumpSound.duty = 0.25f;\n\n            engine.getAudioEngine().playEvent(jumpSound);\n\n            // Jump logic\n            jump();\n        }\n    }\n};\n
    "},{"location":"reference/code_examples/#ui-components","title":"UI Components","text":""},{"location":"reference/code_examples/#simple-menu","title":"Simple Menu","text":"
    class MenuScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::ui::UIVerticalLayout* menu;\n\npublic:\n    void init() override {\n        menu = new pixelroot32::graphics::ui::UIVerticalLayout(40, 60, 160, 160);\n        menu->setPadding(10);\n        menu->setSpacing(8);\n        menu->setNavigationButtons(0, 1);\n        menu->setButtonStyle(\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Cyan,\n            pixelroot32::graphics::Color::White,\n            pixelroot32::graphics::Color::Black\n        );\n\n        menu->addElement(new pixelroot32::graphics::ui::UIButton(\n            \"Start\", 0, 0, 0, 140, 25, []() { startGame(); }\n        ));\n\n        menu->addElement(new pixelroot32::graphics::ui::UIButton(\n            \"Options\", 1, 0, 0, 140, 25, []() { showOptions(); }\n        ));\n\n        addEntity(menu);\n    }\n\n    void update(unsigned long deltaTime) override {\n        menu->handleInput(engine.getInputManager());\n        Scene::update(deltaTime);\n    }\n};\n
    "},{"location":"reference/code_examples/#hud-with-labels","title":"HUD with Labels","text":"
    class GameHUD : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::ui::UILabel* scoreLabel;\n    pixelroot32::graphics::ui::UILabel* livesLabel;\n\npublic:\n    GameHUD()\n        : Entity(0, 0, 240, 240, pixelroot32::core::EntityType::UI_ELEMENT) {\n        setRenderLayer(2);\n\n        scoreLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Score: 0\", 10, 10,\n            pixelroot32::graphics::Color::White, 1\n        );\n\n        livesLabel = new pixelroot32::graphics::ui::UILabel(\n            \"Lives: 3\", 10, 20,\n            pixelroot32::graphics::Color::White, 1\n        );\n    }\n\n    void updateHUD(int score, int lives) {\n        char buffer[32];\n        snprintf(buffer, sizeof(buffer), \"Score: %d\", score);\n        scoreLabel->setText(buffer);\n\n        snprintf(buffer, sizeof(buffer), \"Lives: %d\", lives);\n        livesLabel->setText(buffer);\n    }\n};\n
    "},{"location":"reference/code_examples/#physics","title":"Physics","text":""},{"location":"reference/code_examples/#bouncing-ball","title":"Bouncing Ball","text":"
    class BouncingBall : public pixelroot32::core::PhysicsActor {\npublic:\n    BouncingBall(float x, float y, float radius)\n        : PhysicsActor(x, y, radius * 2, radius * 2) {\n        setRenderLayer(1);\n        setRestitution(0.9f);\n        setFriction(0.05f);\n        setWorldSize(240, 240);\n        setVelocity(50.0f, -30.0f);\n    }\n\n    void update(unsigned long deltaTime) override {\n        float gravity = 200.0f;\n        float dt = deltaTime * 0.001f;\n        setVelocity(vx, vy + gravity * dt);\n        PhysicsActor::update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        int radius = width / 2;\n        renderer.drawFilledCircle(\n            static_cast<int>(x + radius),\n            static_cast<int>(y + radius),\n            radius,\n            pixelroot32::graphics::Color::Cyan\n        );\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n};\n
    "},{"location":"reference/code_examples/#platformer-player","title":"Platformer Player","text":"
    class PlatformerPlayer : public pixelroot32::core::PhysicsActor {\nprivate:\n    bool canJump = true;\n    float jumpForce = 250.0f;\n    float moveSpeed = 100.0f;\n\npublic:\n    PlatformerPlayer(float x, float y)\n        : PhysicsActor(x, y, 16, 16) {\n        setRenderLayer(1);\n        setFriction(0.3f);\n        setWorldSize(240, 240);\n    }\n\n    void update(unsigned long deltaTime) override {\n        auto& input = engine.getInputManager();\n        float dt = deltaTime * 0.001f;\n\n        // Horizontal movement\n        float moveDir = 0.0f;\n        if (input.isButtonDown(Buttons::LEFT)) moveDir -= 1.0f;\n        if (input.isButtonDown(Buttons::RIGHT)) moveDir += 1.0f;\n        setVelocity(moveDir * moveSpeed, vy);\n\n        // Gravity\n        float gravity = 300.0f;\n        setVelocity(vx, vy + gravity * dt);\n\n        // Jump\n        if (input.isButtonPressed(Buttons::A) && canJump) {\n            setVelocity(vx, -jumpForce);\n            canJump = false;\n        }\n\n        PhysicsActor::update(deltaTime);\n\n        // Check if on ground\n        auto collisionInfo = getWorldCollisionInfo();\n        if (collisionInfo.bottom) {\n            canJump = true;\n        }\n    }\n\n    pixelroot32::core::Rect getHitBox() override {\n        return {x, y, width, height};\n    }\n};\n
    "},{"location":"reference/code_examples/#sprites-and-animation","title":"Sprites and Animation","text":""},{"location":"reference/code_examples/#simple-sprite","title":"Simple Sprite","text":"
    // Define sprite data\nstatic const uint16_t PLAYER_SPRITE_DATA[] = {\n    0b00111100,\n    0b01111110,\n    0b11111111,\n    0b11111111,\n    0b11111111,\n    0b01111110,\n    0b00111100,\n    0b00000000\n};\n\nstatic const pixelroot32::graphics::Sprite PLAYER_SPRITE = {\n    PLAYER_SPRITE_DATA, 8, 8\n};\n\n// Draw sprite\nrenderer.drawSprite(PLAYER_SPRITE, 100, 100, \n    pixelroot32::graphics::Color::White);\n
    "},{"location":"reference/code_examples/#sprite-animation","title":"Sprite Animation","text":"
    class AnimatedActor : public pixelroot32::core::Actor {\nprivate:\n    pixelroot32::graphics::SpriteAnimation animation;\n    unsigned long timer = 0;\n    const unsigned long FRAME_DURATION_MS = 100;\n\npublic:\n    AnimatedActor(float x, float y)\n        : Actor(x, y, 8, 8) {\n        setRenderLayer(1);\n        animation.frames = WALK_ANIMATION_FRAMES;\n        animation.frameCount = 3;\n        animation.current = 0;\n    }\n\n    void update(unsigned long deltaTime) override {\n        timer += deltaTime;\n        if (timer >= FRAME_DURATION_MS) {\n            timer -= FRAME_DURATION_MS;\n            animation.step();\n        }\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        const auto* frame = animation.frames[animation.current].sprite;\n        renderer.drawSprite(*frame, static_cast<int>(x), static_cast<int>(y),\n            pixelroot32::graphics::Color::White);\n    }\n};\n
    "},{"location":"reference/code_examples/#camera-and-scrolling","title":"Camera and Scrolling","text":""},{"location":"reference/code_examples/#basic-camera-follow","title":"Basic Camera Follow","text":"
    class ScrollingScene : public pixelroot32::core::Scene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    PlayerActor* player;\n\npublic:\n    void init() override {\n        int screenWidth = engine.getRenderer().getWidth();\n        int screenHeight = engine.getRenderer().getHeight();\n\n        camera = pixelroot32::graphics::Camera2D(screenWidth, screenHeight);\n        camera.setBounds(0, 2000 - screenWidth);\n\n        player = new PlayerActor(100, 100);\n        addEntity(player);\n    }\n\n    void update(unsigned long deltaTime) override {\n        Scene::update(deltaTime);\n        camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        camera.apply(renderer);\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"reference/code_examples/#tilemaps","title":"Tilemaps","text":""},{"location":"reference/code_examples/#simple-tilemap","title":"Simple Tilemap","text":"
    // Define tiles\nstatic const uint16_t TILE_EMPTY_BITS[] = { /* ... */ };\nstatic const uint16_t TILE_GROUND_BITS[] = { /* ... */ };\n\nstatic const pixelroot32::graphics::Sprite TILES[] = {\n    { TILE_EMPTY_BITS, 8, 8 },\n    { TILE_GROUND_BITS, 8, 8 }\n};\n\n// Create tilemap\nstatic uint8_t TILEMAP_INDICES[30 * 20];\nstatic pixelroot32::graphics::TileMap levelTileMap = {\n    TILEMAP_INDICES, 30, 20, TILES, 8, 8, 2\n};\n\n// Initialize\nvoid initTilemap() {\n    for (int i = 0; i < 30 * 20; i++) {\n        TILEMAP_INDICES[i] = 0;\n    }\n    // Set ground row\n    for (int x = 0; x < 30; x++) {\n        TILEMAP_INDICES[19 * 30 + x] = 1; // Ground tile\n    }\n}\n\n// Draw\nrenderer.drawTileMap(levelTileMap, 0, 0, \n    pixelroot32::graphics::Color::White);\n
    "},{"location":"reference/code_examples/#particles","title":"Particles","text":""},{"location":"reference/code_examples/#explosion-effect","title":"Explosion Effect","text":"
    #include <graphics/particles/ParticleEmitter.h>\n#include <graphics/particles/ParticlePresets.h>\n\nclass ExplosionEffect : public pixelroot32::core::Entity {\nprivate:\n    pixelroot32::graphics::particles::ParticleEmitter* emitter;\n\npublic:\n    ExplosionEffect()\n        : Entity(0, 0, 1, 1, pixelroot32::core::EntityType::GENERIC) {\n        setRenderLayer(1);\n        emitter = new pixelroot32::graphics::particles::ParticleEmitter(\n            0, 0,\n            pixelroot32::graphics::particles::ParticlePresets::Explosion()\n        );\n    }\n\n    void trigger(float x, float y) {\n        this->x = x;\n        this->y = y;\n        emitter->burst(x, y, 25);\n    }\n\n    void update(unsigned long deltaTime) override {\n        emitter->update(deltaTime);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        emitter->draw(renderer);\n    }\n};\n
    "},{"location":"reference/code_examples/#object-pooling","title":"Object Pooling","text":""},{"location":"reference/code_examples/#entity-pool","title":"Entity Pool","text":"
    template<typename T, int POOL_SIZE>\nclass EntityPool {\nprivate:\n    T pool[POOL_SIZE];\n    bool inUse[POOL_SIZE];\n    int activeCount = 0;\n\npublic:\n    EntityPool() {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            inUse[i] = false;\n        }\n    }\n\n    T* acquire() {\n        if (activeCount >= POOL_SIZE) return nullptr;\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (!inUse[i]) {\n                inUse[i] = true;\n                activeCount++;\n                return &pool[i];\n            }\n        }\n        return nullptr;\n    }\n\n    void release(T* obj) {\n        for (int i = 0; i < POOL_SIZE; i++) {\n            if (&pool[i] == obj) {\n                inUse[i] = false;\n                activeCount--;\n                obj->isEnabled = false;\n                obj->isVisible = false;\n                break;\n            }\n        }\n    }\n};\n
    "},{"location":"reference/code_examples/#common-patterns","title":"Common Patterns","text":""},{"location":"reference/code_examples/#state-machine","title":"State Machine","text":"
    enum class GameState {\n    MENU,\n    PLAYING,\n    PAUSED,\n    GAME_OVER\n};\n\nclass GameScene : public pixelroot32::core::Scene {\nprivate:\n    GameState currentState = GameState::MENU;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        switch (currentState) {\n            case GameState::MENU:\n                updateMenu();\n                break;\n            case GameState::PLAYING:\n                updateGame();\n                break;\n            case GameState::PAUSED:\n                updatePause();\n                break;\n            case GameState::GAME_OVER:\n                updateGameOver();\n                break;\n        }\n        Scene::update(deltaTime);\n    }\n};\n
    "},{"location":"reference/code_examples/#timer-pattern","title":"Timer Pattern","text":"
    class Timer {\nprivate:\n    unsigned long duration;\n    unsigned long elapsed = 0;\n    bool active = false;\n\npublic:\n    Timer(unsigned long ms) : duration(ms) {}\n\n    void start() {\n        active = true;\n        elapsed = 0;\n    }\n\n    void update(unsigned long deltaTime) {\n        if (active) {\n            elapsed += deltaTime;\n            if (elapsed >= duration) {\n                active = false;\n            }\n        }\n    }\n\n    bool isFinished() const { return !active && elapsed >= duration; }\n    bool isActive() const { return active; }\n    float getProgress() const { return static_cast<float>(elapsed) / duration; }\n};\n
    "},{"location":"reference/code_examples/#see-also","title":"See Also","text":"
    • API Reference Overview - Complete API documentation
    • Game Examples Guide - Learn from complete games
    • Manual - Game Development - Detailed guides
    "},{"location":"reference/game_examples_guide/","title":"Game Examples Guide","text":"

    This guide analyzes the complete game examples included with PixelRoot32, explaining their architecture, patterns, and lessons learned.

    "},{"location":"reference/game_examples_guide/#available-examples","title":"Available Examples","text":"

    PixelRoot32 (in the PixelRoot32 Game Samples project) includes these games and demos:

    • Metroidvania: 2D platformer with multi-layer 4bpp tilemap and tile-based collision (requires PIXELROOT32_ENABLE_4BPP_SPRITES; no scroll/camera)
    • Space Invaders: Full shooter with enemies, projectiles, bunkers and audio
    • Pong: Classic with physics and collisions
    • BrickBreaker: Breakout-style with particles and advanced audio
    • Snake: Grid-based game with entity pooling
    • TicTacToe: Turn-based with simple AI
    • CameraDemo: Platformer with camera and parallax
    • SpritesDemo: 2bpp and 4bpp sprites
    • TileMapDemo: 4bpp tilemaps (with viewport culling)
    "},{"location":"reference/game_examples_guide/#space-invaders","title":"Space Invaders","text":"

    Location: src/examples/SpaceInvaders/

    "},{"location":"reference/game_examples_guide/#architecture","title":"Architecture","text":"

    Space Invaders demonstrates a complete game with multiple systems:

    • Scene Management: SpaceInvadersScene manages game state
    • Actor Hierarchy: PlayerActor, AlienActor, ProjectileActor, BunkerActor
    • Collision System: Uses collision layers for player, enemies, projectiles
    • Audio Integration: Sound effects for shooting, explosions, music
    • Background: Starfield (code-generated star pattern) or tilemap
    "},{"location":"reference/game_examples_guide/#key-systems","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#collision-layers","title":"Collision Layers","text":"
    namespace Layers {\n    constexpr uint16_t PLAYER = 0x0001;\n    constexpr uint16_t ALIEN = 0x0002;\n    constexpr uint16_t PROJECTILE = 0x0004;\n    constexpr uint16_t BUNKER = 0x0008;\n}\n\n// Player can collide with aliens and bunkers\nplayer->setCollisionLayer(Layers::PLAYER);\nplayer->setCollisionMask(Layers::ALIEN | Layers::BUNKER);\n\n// Projectiles can hit aliens and bunkers\nprojectile->setCollisionLayer(Layers::PROJECTILE);\nprojectile->setCollisionMask(Layers::ALIEN | Layers::BUNKER);\n
    "},{"location":"reference/game_examples_guide/#entity-management","title":"Entity Management","text":"
    • Uses object pooling for projectiles
    • Manages alien formation with grid layout
    • Handles game state (playing, game over)
    "},{"location":"reference/game_examples_guide/#audio-integration","title":"Audio Integration","text":"
    • Background music using MusicPlayer
    • Sound effects for various events
    • Audio events triggered on collisions
    "},{"location":"reference/game_examples_guide/#patterns-used","title":"Patterns Used","text":"
    • Object Pooling: Projectiles are pooled and reused
    • State Machine: Game states (playing, game over, victory)
    • Grid Layout: Alien formation uses grid-based positioning
    • Event-Driven Audio: Sounds triggered by game events
    "},{"location":"reference/game_examples_guide/#lessons-learned","title":"Lessons Learned","text":"
    • Collision layers are essential for complex games
    • Object pooling improves performance
    • Starfield or tilemap backgrounds are efficient
    • Audio enhances game feel significantly
    "},{"location":"reference/game_examples_guide/#metroidvania","title":"Metroidvania","text":"

    Location: src/examples/Games/Metroidvania/

    Assets: Sprites and tilesets for this example come from the Tiny Metroidvania 8x8 pack by Kenmi (kenmi-art.itch.io).

    "},{"location":"reference/game_examples_guide/#architecture_1","title":"Architecture","text":"

    Metroidvania is a 2D platformer example with multi-layer tilemap and optimizations aimed at ESP32. It does not use scroll or camera; the level is drawn with a fixed origin (0,0).

    • Scene: MetroidvaniaScene with a single PlayerActor and several tilemap layers (background, platforms, details, stairs).
    • PlayerActor: Horizontal and vertical movement, stairs, tile-based collision (no rectangle lists).
    • Tilemap: 4bpp (TileMap4bpp), with viewport culling and palette cache in the engine. Level 40\u00d730 tiles (320\u00d7240 px).
    • No camera: The view does not follow the player; for scroll you would use Camera2D and apply offset in the renderer (as in CameraDemo).
    "},{"location":"reference/game_examples_guide/#engine-features-used","title":"Engine features used","text":"
    • Tile-based collision: Direct tile checks around the player (getTileAt), instead of iterating over platformRects.
    • 4bpp sprites: Player with animations (idle, run, jump) from generated headers (e.g. Sprite Compiler).
    • Rendering optimizations: Viewport culling in drawTileMap, optimized 4bpp drawSprite, layers culled by viewport.
    • Optional: Scene arena, DMA, IRAM_ATTR on critical paths (per example optimization plan).
    "},{"location":"reference/game_examples_guide/#patterns-used_1","title":"Patterns used","text":"
    • Tile-based collision: Single O(1) access per tile instead of O(N) rectangles.
    • Stair detection: Single result reused for collision and state change.
    • Simplified hitbox: Fewer vertical check points (head and feet).
    "},{"location":"reference/game_examples_guide/#lessons-learned_1","title":"Lessons learned","text":"
    • Tile-based collision scales better than rectangle lists on large levels.
    • Viewport and 4bpp optimizations improve FPS on ESP32.
    • Metroidvania serves as a reference for platformers with tilemap and camera.
    "},{"location":"reference/game_examples_guide/#pong","title":"Pong","text":"

    Location: src/examples/Pong/

    "},{"location":"reference/game_examples_guide/#architecture_2","title":"Architecture","text":"

    Pong demonstrates physics and collision handling:

    • PhysicsActor: Ball uses PhysicsActor for automatic physics
    • Collision Callbacks: Paddles and ball handle collisions
    • Score System: Simple score tracking and display
    • Game State: Reset and game over handling
    "},{"location":"reference/game_examples_guide/#key-systems_1","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#physics-setup","title":"Physics Setup","text":"
    class BallActor : public pixelroot32::core::PhysicsActor {\npublic:\n    BallActor(float x, float y, float speed, int radius)\n        : PhysicsActor(x, y, radius * 2, radius * 2) {\n        setRestitution(0.8f);  // Bouncy\n        setFriction(0.1f);     // Low friction\n        setWorldSize(240, 240);\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#collision-response","title":"Collision Response","text":"
    void BallActor::onCollision(pixelroot32::core::Actor* other) {\n    // Adjust ball position\n    // Modify velocity based on impact point\n    // Play bounce sound\n}\n
    "},{"location":"reference/game_examples_guide/#patterns-used_2","title":"Patterns Used","text":"
    • Physics Integration: Uses PhysicsActor for automatic movement
    • Collision Response: Custom collision handling
    • Score Management: Simple state tracking
    • Audio Feedback: Sound on collision
    "},{"location":"reference/game_examples_guide/#lessons-learned_2","title":"Lessons Learned","text":"
    • PhysicsActor simplifies physics-based games
    • Collision callbacks allow custom response logic
    • Simple games can be very effective
    "},{"location":"reference/game_examples_guide/#snake","title":"Snake","text":"

    Location: src/examples/Snake/

    "},{"location":"reference/game_examples_guide/#architecture_3","title":"Architecture","text":"

    Snake demonstrates entity pooling and grid-based movement:

    • Entity Pooling: Snake segments are pooled
    • Grid Movement: Movement constrained to grid
    • Game Logic: Food spawning, collision detection
    • State Management: Game over, reset functionality
    "},{"location":"reference/game_examples_guide/#key-systems_2","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#entity-pooling","title":"Entity Pooling","text":"
    class SnakeScene {\nprivate:\n    std::vector<SnakeSegmentActor*> segmentPool;\n    std::vector<SnakeSegmentActor*> snakeSegments;\n\n    void resetGame() {\n        // Reuse pooled segments\n        for (int i = 0; i < initialLength; ++i) {\n            SnakeSegmentActor* segment = segmentPool[i];\n            segment->resetAlive();\n            snakeSegments.push_back(segment);\n            addEntity(segment);\n        }\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#grid-based-movement","title":"Grid-Based Movement","text":"
    class SnakeSegmentActor : public pixelroot32::core::Actor {\nprivate:\n    int cellX, cellY;  // Grid position\n\npublic:\n    void setCellPosition(int x, int y) {\n        cellX = x;\n        cellY = y;\n        // Convert to world position\n        this->x = cellX * CELL_SIZE;\n        this->y = cellY * CELL_SIZE;\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#patterns-used_3","title":"Patterns Used","text":"
    • Object Pooling: Segments are pre-allocated and reused
    • Grid System: Discrete grid-based movement
    • Linked List: Snake segments form a linked structure
    • Food Spawning: Random food placement with collision checking
    "},{"location":"reference/game_examples_guide/#lessons-learned_3","title":"Lessons Learned","text":"
    • Entity pooling is essential for dynamic entities
    • Grid-based movement simplifies collision detection
    • Pre-allocation avoids memory fragmentation
    "},{"location":"reference/game_examples_guide/#tictactoe","title":"TicTacToe","text":"

    Location: src/examples/TicTacToe/

    "},{"location":"reference/game_examples_guide/#architecture_4","title":"Architecture","text":"

    TicTacToe demonstrates turn-based logic and simple AI:

    • Turn Management: Player vs AI turns
    • Game Board: 3x3 grid representation
    • Win Detection: Check for winning conditions
    • Simple AI: Random move selection
    "},{"location":"reference/game_examples_guide/#key-systems_3","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#board-representation","title":"Board Representation","text":"
    class TicTacToeScene {\nprivate:\n    int board[3][3];  // 0=empty, 1=X, 2=O\n    bool playerTurn = true;\n\n    bool makeMove(int row, int col, int player) {\n        if (board[row][col] == 0) {\n            board[row][col] = player;\n            return true;\n        }\n        return false;\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#win-detection","title":"Win Detection","text":"
    int checkWinner() {\n    // Check rows\n    for (int i = 0; i < 3; i++) {\n        if (board[i][0] == board[i][1] && board[i][1] == board[i][2]) {\n            return board[i][0];\n        }\n    }\n    // Check columns, diagonals...\n    return 0; // No winner\n}\n
    "},{"location":"reference/game_examples_guide/#patterns-used_4","title":"Patterns Used","text":"
    • State Machine: Turn-based state management
    • Grid Logic: 2D array for board representation
    • Simple AI: Random valid move selection
    • UI Integration: Buttons for player input
    "},{"location":"reference/game_examples_guide/#lessons-learned_4","title":"Lessons Learned","text":"
    • Turn-based games are straightforward to implement
    • Simple AI can be effective for basic games
    • Grid-based logic is easy to reason about
    "},{"location":"reference/game_examples_guide/#camerademo","title":"CameraDemo","text":"

    Location: src/examples/CameraDemo/

    "},{"location":"reference/game_examples_guide/#architecture_5","title":"Architecture","text":"

    CameraDemo demonstrates scrolling and parallax:

    • Camera2D: Camera following player
    • Tilemap: Level built with tilemap
    • Parallax: Multiple background layers
    • Platformer Physics: Player with jumping and gravity
    "},{"location":"reference/game_examples_guide/#key-systems_4","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#camera-setup","title":"Camera Setup","text":"
    class CameraDemoScene {\nprivate:\n    pixelroot32::graphics::Camera2D camera;\n    float levelWidth;\n\npublic:\n    void init() override {\n        camera = pixelroot32::graphics::Camera2D(240, 240);\n        camera.setBounds(0, levelWidth - 240);\n    }\n\n    void update(unsigned long deltaTime) override {\n        camera.followTarget(player->x, player->y);\n    }\n\n    void draw(pixelroot32::graphics::Renderer& renderer) override {\n        camera.apply(renderer);\n        renderer.drawTileMap(levelTileMap, 0, 0, Color::White);\n        Scene::draw(renderer);\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#platformer-physics","title":"Platformer Physics","text":"
    class PlayerCube : public pixelroot32::core::PhysicsActor {\npublic:\n    void update(unsigned long deltaTime) override {\n        // Input handling\n        // Gravity application\n        // Jump logic\n        // Platform collision\n        PhysicsActor::update(deltaTime);\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#patterns-used_5","title":"Patterns Used","text":"
    • Camera Following: Dead-zone camera following
    • Tilemap Rendering: Efficient level rendering
    • Parallax Scrolling: Multiple background layers
    • Platform Collision: Custom collision with platforms
    "},{"location":"reference/game_examples_guide/#lessons-learned_5","title":"Lessons Learned","text":"
    • Camera system enables large levels
    • Tilemaps are efficient for level data
    • Parallax adds depth to 2D games
    • Platform collision requires custom logic
    "},{"location":"reference/game_examples_guide/#spritesdemo","title":"SpritesDemo","text":"

    Location: src/examples/SpritesDemo/

    "},{"location":"reference/game_examples_guide/#architecture_6","title":"Architecture","text":"

    SpritesDemo showcases advanced sprite formats:

    • 2bpp Sprites: 4-color sprite format
    • 4bpp Sprites: 16-color sprite format (if enabled)
    • Animation: Sprite animation examples
    • Format Comparison: Side-by-side format display
    "},{"location":"reference/game_examples_guide/#key-systems_5","title":"Key Systems","text":""},{"location":"reference/game_examples_guide/#2bpp-sprite-usage","title":"2bpp Sprite Usage","text":"
    #ifdef PIXELROOT32_ENABLE_2BPP_SPRITES\nstatic const pixelroot32::graphics::Sprite2bpp SPRITE_2BPP = {\n    SPRITE_DATA,\n    SPRITE_PALETTE,\n    16, 32, 4\n};\n\nrenderer.drawSprite(SPRITE_2BPP, x, y, false);\n#endif\n
    "},{"location":"reference/game_examples_guide/#animation-display","title":"Animation Display","text":"
    class SpritesDemoActor : public pixelroot32::core::Entity {\nprivate:\n    unsigned long timer = 0;\n    uint8_t currentFrame = 0;\n\npublic:\n    void update(unsigned long deltaTime) override {\n        timer += deltaTime;\n        if (timer >= 150) {\n            timer -= 150;\n            currentFrame = (currentFrame + 1) % 9;\n        }\n    }\n};\n
    "},{"location":"reference/game_examples_guide/#patterns-used_6","title":"Patterns Used","text":"
    • Format Comparison: Shows different sprite formats
    • Animation Loop: Frame-based animation
    • Conditional Compilation: Uses build flags
    "},{"location":"reference/game_examples_guide/#lessons-learned_6","title":"Lessons Learned","text":"
    • Advanced formats provide more color options
    • Animation is straightforward with frame arrays
    • Build flags enable/disable experimental features
    "},{"location":"reference/game_examples_guide/#common-patterns-across-examples","title":"Common Patterns Across Examples","text":""},{"location":"reference/game_examples_guide/#screen-resolution","title":"Screen Resolution","text":"

    All examples are configured for a 240x240 screen resolution.

    "},{"location":"reference/game_examples_guide/#scene-initialization","title":"Scene Initialization","text":"

    All examples follow this pattern:

    void init() override {\n    // 1. Set palette\n    pixelroot32::graphics::setPalette(PaletteType::NES);\n\n    // 2. Create background entity\n    addEntity(new BackgroundEntity());\n\n    // 3. Create game entities\n    player = new PlayerActor(...);\n    addEntity(player);\n\n    // 4. Initialize game state\n    resetGame();\n}\n
    "},{"location":"reference/game_examples_guide/#update-pattern","title":"Update Pattern","text":"
    void update(unsigned long deltaTime) override {\n    // 1. Process input\n    handleInput();\n\n    // 2. Update game logic\n    updateGameLogic();\n\n    // 3. Call parent update (updates all entities)\n    Scene::update(deltaTime);\n\n    // 4. Post-update logic\n    checkGameState();\n}\n
    "},{"location":"reference/game_examples_guide/#draw-pattern","title":"Draw Pattern","text":"
    void draw(pixelroot32::graphics::Renderer& renderer) override {\n    // 1. Apply camera (if used)\n    camera.apply(renderer);\n\n    // 2. Draw background/tilemap\n    renderer.drawTileMap(background, 0, 0, Color::White);\n\n    // 3. Call parent draw (draws all entities)\n    Scene::draw(renderer);\n\n    // 4. Draw UI/HUD\n    drawHUD(renderer);\n}\n
    "},{"location":"reference/game_examples_guide/#learning-path","title":"Learning Path","text":""},{"location":"reference/game_examples_guide/#beginner-examples","title":"Beginner Examples","text":"
    1. Pong: Basic physics and collisions
    2. TicTacToe: Turn-based logic
    3. Snake: Entity pooling and grid
    "},{"location":"reference/game_examples_guide/#intermediate-examples","title":"Intermediate Examples","text":"
    1. CameraDemo: Camera and parallax
    2. SpritesDemo: 2bpp and 4bpp formats
    3. BrickBreaker: Physics, particles and audio
    "},{"location":"reference/game_examples_guide/#advanced-examples","title":"Advanced Examples","text":"
    1. Space Invaders: Full game (1bpp sprites, collisions, audio)
    2. Metroidvania: Platformer with multi-layer 4bpp tilemap, tile-based collision and ESP32 optimizations (no scroll/camera)
    "},{"location":"reference/game_examples_guide/#code-study-recommendations","title":"Code Study Recommendations","text":""},{"location":"reference/game_examples_guide/#for-learning-physics","title":"For Learning Physics","text":"
    • Study Pong/BallActor.cpp - PhysicsActor usage
    • Study CameraDemo/PlayerCube.cpp - Platformer physics
    "},{"location":"reference/game_examples_guide/#for-learning-collisions","title":"For Learning Collisions","text":"
    • Study SpaceInvaders - Complex collision layers
    • Study Pong - Simple collision response
    "},{"location":"reference/game_examples_guide/#for-learning-memory-management","title":"For Learning Memory Management","text":"
    • Study Snake/SnakeScene.cpp - Entity pooling
    • Study SpaceInvaders - Projectile pooling
    "},{"location":"reference/game_examples_guide/#for-learning-audio","title":"For Learning Audio","text":"
    • Study SpaceInvaders - Music and sound effects
    • Study Pong - Simple audio integration
    "},{"location":"reference/game_examples_guide/#for-learning-ui","title":"For Learning UI","text":"
    • Study TicTacToe - Button-based UI
    • Study menu scenes - Layout usage
    "},{"location":"reference/game_examples_guide/#extending-examples","title":"Extending Examples","text":""},{"location":"reference/game_examples_guide/#adding-features","title":"Adding Features","text":"
    • Pong: Add power-ups, multiple balls
    • Snake: Add obstacles, multiple food types
    • Space Invaders: Add boss battles, power-ups
    "},{"location":"reference/game_examples_guide/#creating-variations","title":"Creating Variations","text":"
    • Pong: Make it vertical, add walls
    • Snake: Change to hexagonal grid
    • TicTacToe: Make it 4x4 or 5x5
    "},{"location":"reference/game_examples_guide/#best-practices-from-examples","title":"Best Practices from Examples","text":"
    1. Pre-allocate Resources: All examples pre-allocate entities
    2. Use Object Pooling: For frequently created/destroyed entities
    3. Organize by Layers: Clear collision layer organization
    4. Separate Concerns: Game logic separate from rendering
    5. State Management: Clear game state handling
    "},{"location":"reference/game_examples_guide/#see-also","title":"See Also","text":"
    • Code Examples - Reusable code snippets
    • API Reference Overview - Complete API documentation
    • Manual - Game Development - Detailed guides

    Note: All example code is available in the src/examples/ directory of the PixelRoot32 Game Samples project.

    "},{"location":"resources/available_tools/","title":"Available Tools","text":"

    This guide documents tools available to facilitate PixelRoot32 game development.

    "},{"location":"resources/available_tools/#sprite-compiler-pr32-sprite-compiler","title":"Sprite Compiler (pr32-sprite-compiler)","text":"

    The Sprite Compiler converts PNG images to PixelRoot32 sprite data formats, making it easy to create sprites from image files.

    Read more in the Sprite Compiler Guide

    From Source:

    git clone https://github.com/Gperez88/pr32-sprite-compiler.git\ncd pr32-sprite-compiler\nnpm install\nnpm link  # Optional: install globally\n

    As NPM Package:

    npm install -g pr32-sprite-compiler\n
    "},{"location":"resources/available_tools/#basic-usage","title":"Basic Usage","text":"

    Command Line:

    pr32-sprite-compiler input.png output.h\n

    With Options:

    pr32-sprite-compiler input.png output.h --format 1bpp --name MY_SPRITE\n
    "},{"location":"resources/available_tools/#supported-formats","title":"Supported Formats","text":"
    • 1bpp (default): Monochrome, most memory-efficient
    • 2bpp: 4 colors per sprite (requires PIXELROOT32_ENABLE_2BPP_SPRITES)
    • 4bpp: 16 colors per sprite (requires PIXELROOT32_ENABLE_4BPP_SPRITES)
    "},{"location":"resources/available_tools/#output-format","title":"Output Format","text":"

    The compiler generates C++ header files with sprite data:

    // output.h\n#ifndef SPRITE_DATA_H\n#define SPRITE_DATA_H\n\n#include <stdint.h>\n\nstatic const uint16_t MY_SPRITE_DATA[] = {\n    0b00111100,\n    0b01111110,\n    0b11111111,\n    // ... more rows\n};\n\nstatic const pixelroot32::graphics::Sprite MY_SPRITE = {\n    MY_SPRITE_DATA,\n    8,  // width\n    8   // height\n};\n\n#endif\n
    "},{"location":"resources/available_tools/#advanced-options","title":"Advanced Options","text":"

    Batch Processing:

    pr32-sprite-compiler --batch sprites/*.png --output-dir sprites/out/\n

    Custom Palette:

    pr32-sprite-compiler input.png output.h --palette custom_palette.json\n

    Sprite Sheet:

    pr32-sprite-compiler sheet.png output.h --sheet 8x8 --count 16\n
    "},{"location":"resources/available_tools/#gui-version","title":"GUI Version","text":"

    If available, a GUI version provides visual feedback:

    • Drag and drop images
    • Preview sprite data
    • Adjust settings visually
    • Export to header files
    "},{"location":"resources/available_tools/#step-by-step-example","title":"Step-by-Step Example","text":"
    1. Create or find a PNG image (8x8, 16x16, etc.)

    2. Run the compiler:

    pr32-sprite-compiler player.png player_sprite.h --name PLAYER_SPRITE\n
    1. Include in your project:
    #include \"player_sprite.h\"\n\nvoid draw() {\n    renderer.drawSprite(PLAYER_SPRITE, 100, 100, Color::White);\n}\n
    "},{"location":"resources/available_tools/#troubleshooting","title":"Troubleshooting","text":"

    Image too large:

    • Sprites must be \u2264 16 pixels wide for 1bpp
    • Reduce image size or split into multiple sprites

    Colors not converting correctly:

    • Ensure image uses indexed colors
    • Use black/white for 1bpp
    • Use 4 colors for 2bpp, 16 for 4bpp

    Output file not found:

    • Check write permissions
    • Verify output path exists
    "},{"location":"resources/available_tools/#future-tools","title":"Future Tools","text":""},{"location":"resources/available_tools/#music-compiler-planned","title":"Music Compiler (Planned)","text":"

    A tool to convert music files or MIDI to PixelRoot32 MusicTrack format.

    Planned Features:

    • MIDI to MusicTrack conversion
    • Visual music editor
    • Instrument preset management
    • Export to C++ header files
    "},{"location":"resources/available_tools/#tilemap-compiler-planned","title":"Tilemap Compiler (Planned)","text":"

    A tool to create tilemaps from image files or tile editors.

    Planned Features:

    • Image to tilemap conversion
    • Tile editor integration
    • Export to C++ arrays
    • Collision data generation
    "},{"location":"resources/available_tools/#other-planned-tools","title":"Other Planned Tools","text":"
    • Save System Generator: Generate save/load code
    • Asset Packer: Bundle assets for distribution
    • Performance Profiler: Analyze game performance
    "},{"location":"resources/available_tools/#using-tools-in-development","title":"Using Tools in Development","text":""},{"location":"resources/available_tools/#workflow-integration","title":"Workflow Integration","text":"

    Typical Workflow:

    1. Create/edit sprites in image editor
    2. Compile sprites to C++ headers
    3. Include headers in project
    4. Use sprites in code

    Automation:

    # Build script example\n#!/bin/bash\npr32-sprite-compiler assets/sprites/*.png --output-dir src/sprites/\n# Continue with build...\n
    "},{"location":"resources/available_tools/#best-practices","title":"Best Practices","text":"
    • Organize assets: Keep source images separate from generated code
    • Version control: Commit generated headers, not source images (or both)
    • Naming conventions: Use consistent naming for sprites
    • Batch processing: Process multiple sprites at once when possible
    "},{"location":"resources/available_tools/#see-also","title":"See Also","text":"
    • Sprite Compiler Documentation - Detailed sprite compiler guide
    • Manual - Sprites and Animation - Using sprites in games
    • Troubleshooting - Common tool issues

    Note: Tool availability may vary. Check the PixelRoot32 repository for the latest tool information.

    "},{"location":"resources/faq/","title":"Frequently Asked Questions","text":"

    Common questions about PixelRoot32, organized by category.

    "},{"location":"resources/faq/#general","title":"General","text":""},{"location":"resources/faq/#what-is-pixelroot32","title":"What is PixelRoot32?","text":"

    PixelRoot32 is a lightweight, modular 2D game engine designed for ESP32 microcontrollers. It provides a complete game development framework with rendering, audio, physics, input, and UI systems, optimized for limited hardware resources.

    "},{"location":"resources/faq/#what-platforms-does-it-support","title":"What platforms does it support?","text":"
    • ESP32: Primary platform (TFT displays, GPIO buttons, DAC/I2S audio)
    • Native/Desktop: Development platform (SDL2, keyboard, SDL2 audio)
    "},{"location":"resources/faq/#what-kind-of-games-can-i-make","title":"What kind of games can I make?","text":"

    PixelRoot32 is ideal for: - Retro/arcade-style games - 2D platformers - Shooters - Puzzle games - Simple RPGs - Educational games

    See Limitations and Considerations for what's not suitable.

    "},{"location":"resources/faq/#is-it-free-to-use","title":"Is it free to use?","text":"

    Yes, PixelRoot32 is open source and licensed under the MIT License. You can use it freely for personal and commercial projects.

    "},{"location":"resources/faq/#where-can-i-find-the-source-code","title":"Where can I find the source code?","text":"
    • Engine: https://github.com/Gperez88/PixelRoot32-Game-Engine
    • Samples: https://github.com/Gperez88/PixelRoot32-Game-Samples
    • Documentation: https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Docs
    "},{"location":"resources/faq/#installation-and-configuration","title":"Installation and Configuration","text":""},{"location":"resources/faq/#how-do-i-install-pixelroot32","title":"How do I install PixelRoot32?","text":"

    See Your First Project for detailed installation instructions.

    Quick answer: 1. Install PlatformIO in VS Code 2. Create new ESP32 project 3. Add library dependency: gperez88/PixelRoot32-Game-Engine@0.2.0-dev 4. Configure hardware in platformio.ini

    "},{"location":"resources/faq/#what-version-should-i-use","title":"What version should I use?","text":"

    Use the exact version 0.2.0-dev. Do NOT use ^ or fuzzy versioning, as the API may change.

    "},{"location":"resources/faq/#how-do-i-configure-my-display","title":"How do I configure my display?","text":"

    Configure TFT_eSPI via build flags in platformio.ini. See Your First Project for examples.

    "},{"location":"resources/faq/#how-do-i-set-up-audio","title":"How do I set up audio?","text":"

    Choose an audio backend: - ESP32_DAC: Simple, one pin (GPIO 25 or 26) - ESP32_I2S: Higher quality, requires external DAC - SDL2_AudioBackend: For Native/PC development

    See Audio for details.

    "},{"location":"resources/faq/#development","title":"Development","text":""},{"location":"resources/faq/#how-do-i-create-a-scene","title":"How do I create a scene?","text":"

    Inherit from pixelroot32::core::Scene and implement init(), update(), and draw(). See Scenes and Entities.

    "},{"location":"resources/faq/#how-do-i-add-entities-to-a-scene","title":"How do I add entities to a scene?","text":"

    Create entities and call addEntity() in init(). The scene manages them automatically.

    "},{"location":"resources/faq/#whats-the-difference-between-entity-actor-and-physicsactor","title":"What's the difference between Entity, Actor, and PhysicsActor?","text":"
    • Entity: Base class, can be drawn and updated
    • Actor: Entity with collision detection
    • PhysicsActor: Actor with automatic physics (velocity, gravity, friction)

    See Scenes and Entities for details.

    "},{"location":"resources/faq/#how-do-i-handle-input","title":"How do I handle input?","text":"

    Access InputManager through the engine:

    auto& input = engine.getInputManager();\nif (input.isButtonPressed(Buttons::A)) {\n    // Handle input\n}\n

    See Input and Control.

    "},{"location":"resources/faq/#how-do-i-play-sounds","title":"How do I play sounds?","text":"

    Create an AudioEvent and play it:

    pixelroot32::audio::AudioEvent sound{};\nsound.type = pixelroot32::audio::WaveType::PULSE;\nsound.frequency = 800.0f;\nsound.duration = 0.1f;\nengine.getAudioEngine().playEvent(sound);\n

    See Audio.

    "},{"location":"resources/faq/#how-do-i-create-sprites","title":"How do I create sprites?","text":"

    Define sprite data manually or use the Sprite Compiler tool. See Sprites and Animation.

    "},{"location":"resources/faq/#can-i-use-images-instead-of-manual-sprite-data","title":"Can I use images instead of manual sprite data?","text":"

    Yes, use the Sprite Compiler tool to convert PNG images to sprite data. See Available Tools.

    "},{"location":"resources/faq/#performance","title":"Performance","text":""},{"location":"resources/faq/#why-is-my-game-running-slowly","title":"Why is my game running slowly?","text":"

    Common causes: - Too many entities (MAX_ENTITIES = 32) - Too many draw calls - Expensive calculations in update() - Memory issues

    See Performance Tuning for solutions.

    "},{"location":"resources/faq/#what-fps-should-i-target","title":"What FPS should I target?","text":"

    30-60 FPS is typical. Lower complexity games can achieve 60 FPS, more complex games may need to target 30 FPS.

    "},{"location":"resources/faq/#how-do-i-optimize-my-game","title":"How do I optimize my game?","text":"
    • Use object pooling
    • Implement viewport culling
    • Reduce entity count
    • Cache calculations
    • Use tilemaps for backgrounds

    See Performance Tuning.

    "},{"location":"resources/faq/#memory","title":"Memory","text":""},{"location":"resources/faq/#why-do-i-get-out-of-memory-errors","title":"Why do I get \"out of memory\" errors?","text":"

    ESP32 has limited RAM (~320KB). Solutions: - Use object pooling - Store data in flash (const/constexpr) - Reduce entity count - Avoid dynamic allocation

    See Memory Management.

    "},{"location":"resources/faq/#what-is-max_entities","title":"What is MAX_ENTITIES?","text":"

    MAX_ENTITIES = 32 is a hard limit per scene. This includes all entities: actors, UI elements, particles, etc.

    Solutions: - Use object pooling to reuse entities - Disable entities instead of removing - Combine multiple entities into one

    "},{"location":"resources/faq/#how-do-i-check-available-memory","title":"How do I check available memory?","text":"
    #ifdef PLATFORM_ESP32\nSerial.print(\"Free heap: \");\nSerial.println(ESP.getFreeHeap());\n#endif\n
    "},{"location":"resources/faq/#hardware","title":"Hardware","text":""},{"location":"resources/faq/#which-esp32-board-should-i-use","title":"Which ESP32 board should I use?","text":"

    Any ESP32 board works. Common choices: - ESP32-WROOM-32 - ESP32-WROVER (more RAM) - ESP32-DevKit

    "},{"location":"resources/faq/#which-display-should-i-use","title":"Which display should I use?","text":"

    Popular choices: - ST7789: 240x240, good quality - ST7735: 128x128, smaller/cheaper - ILI9341: 240x320, larger

    See Platforms and Drivers.

    "},{"location":"resources/faq/#how-many-buttons-do-i-need","title":"How many buttons do I need?","text":"

    Minimum: 4 (UP, DOWN, LEFT, RIGHT) Recommended: 6 (add A and B buttons) More buttons can be added if needed.

    "},{"location":"resources/faq/#can-i-use-analog-joysticks","title":"Can I use analog joysticks?","text":"

    Not directly supported. You can read analog pins manually and convert to digital input, but the engine expects digital buttons.

    "},{"location":"resources/faq/#troubleshooting","title":"Troubleshooting","text":""},{"location":"resources/faq/#my-display-is-blank-whats-wrong","title":"My display is blank. What's wrong?","text":"
    1. Check wiring connections
    2. Verify pin numbers in platformio.ini
    3. Lower SPI frequency
    4. Check display type matches hardware
    5. Verify power supply

    See Troubleshooting.

    "},{"location":"resources/faq/#buttons-dont-work-why","title":"Buttons don't work. Why?","text":"
    1. Check button wiring
    2. Verify pin numbers in InputConfig
    3. Check pull-up/pull-down resistors
    4. Ensure InputManager is being updated
    5. Test with isButtonDown() vs isButtonPressed()
    "},{"location":"resources/faq/#audio-is-distorted-how-do-i-fix-it","title":"Audio is distorted. How do I fix it?","text":"
    1. Lower volume levels
    2. Reduce sample rate (try 11025 Hz)
    3. Check for too many simultaneous sounds
    4. Verify hardware connections
    5. Check power supply
    "},{"location":"resources/faq/#game-crashes-randomly-whats-happening","title":"Game crashes randomly. What's happening?","text":"

    Common causes: - Out of memory - Too many entities - Infinite loops - Stack overflow - Watchdog timeout

    See Troubleshooting for debugging techniques.

    "},{"location":"resources/faq/#advanced","title":"Advanced","text":""},{"location":"resources/faq/#can-i-extend-the-engine","title":"Can I extend the engine?","text":"

    Yes, PixelRoot32 is designed to be extensible. You can: - Create custom display drivers - Create custom audio backends - Extend existing systems

    See Extensibility.

    "},{"location":"resources/faq/#can-i-use-3d-graphics","title":"Can I use 3D graphics?","text":"

    No, PixelRoot32 is 2D-only. It's designed for sprite-based 2D games.

    "},{"location":"resources/faq/#can-i-add-networking","title":"Can I add networking?","text":"

    Not currently supported. The engine focuses on single-player games.

    "},{"location":"resources/faq/#can-i-save-game-data","title":"Can I save game data?","text":"

    Not currently supported. A save system is planned for future versions.

    "},{"location":"resources/faq/#can-i-use-multiple-scenes-at-once","title":"Can I use multiple scenes at once?","text":"

    Yes, use SceneManager to push/pop scenes. This is useful for menus and pause screens.

    "},{"location":"resources/faq/#getting-help","title":"Getting Help","text":""},{"location":"resources/faq/#where-can-i-get-help","title":"Where can I get help?","text":"
    • Documentation: This documentation site
    • Examples: Study example games in the samples project
    • Discord: Community Discord server
    • GitHub: Open an issue for bugs
    "},{"location":"resources/faq/#how-do-i-report-a-bug","title":"How do I report a bug?","text":"

    Create a detailed bug report with: - Platform (ESP32 or Native) - Hardware details - Minimal reproduction code - Error messages - Expected vs actual behavior

    "},{"location":"resources/faq/#can-i-contribute","title":"Can I contribute?","text":"

    Yes! PixelRoot32 is open source. Check the main repository for contribution guidelines.

    "},{"location":"resources/faq/#see-also","title":"See Also","text":"
    • Troubleshooting - Detailed problem solving
    • Limitations and Considerations - What the engine can/can't do
    • Getting Started - Start here if you're new
    • Manual - Complete development guides

    Can't find your question? Check the Troubleshooting guide or ask on the Discord server.

    "},{"location":"resources/limitations_and_considerations/","title":"Limitations and Considerations","text":"

    This document honestly documents what PixelRoot32 can and cannot do, helping you make informed decisions about using the engine.

    "},{"location":"resources/limitations_and_considerations/#hardware-limitations-esp32","title":"Hardware Limitations (ESP32)","text":""},{"location":"resources/limitations_and_considerations/#memory-constraints","title":"Memory Constraints","text":"

    RAM: - Available: ~320KB total (varies by ESP32 model) - Heap: Limited and fragmented over time - Stack: ~8KB, avoid large stack allocations - Impact: Limits entity count, sprite data, and dynamic allocation

    Flash: - Available: 4MB+ (varies by model) - Usage: Program code, sprite data, assets - Impact: Large games may approach limits

    Recommendations: - Use object pooling - Store data in flash (const/constexpr) - Avoid dynamic allocation in game loop - Keep entity count low

    "},{"location":"resources/limitations_and_considerations/#cpu-limitations","title":"CPU Limitations","text":"

    Performance: - Clock Speed: 240MHz (typically) - Single-threaded: One core handles everything - Target FPS: 30-60 FPS (depends on complexity) - Frame Budget: ~16-33ms per frame at 60 FPS

    Impact: - Complex games may struggle - Many entities reduce performance - Expensive calculations hurt FPS

    Recommendations: - Optimize rendering - Reduce entity count - Cache calculations - Profile on hardware

    "},{"location":"resources/limitations_and_considerations/#display-limitations","title":"Display Limitations","text":"

    Supported Displays: - TFT displays via SPI (ST7735, ST7789, ILI9341, etc.) - Limited to SPI displays - Resolution typically 128x128 to 320x240

    Constraints: - SPI communication speed limits - Display initialization complexity - Power consumption

    "},{"location":"resources/limitations_and_considerations/#audio-limitations","title":"Audio Limitations","text":"

    Hardware: - Internal DAC: Lower quality, simple setup - I2S: Higher quality, requires external DAC - Sample rates: 11025 Hz (DAC) or 22050 Hz (I2S)

    Constraints: - 4 channels total (2 Pulse, 1 Triangle, 1 Noise) - Music uses one channel - Limited simultaneous sounds - Quality limited by hardware

    "},{"location":"resources/limitations_and_considerations/#software-limitations","title":"Software Limitations","text":""},{"location":"resources/limitations_and_considerations/#entity-system","title":"Entity System","text":"

    MAX_ENTITIES = 32 per scene - Hard limit, cannot be changed easily - Applies to all entities (actors, UI, particles, etc.) - Must manage entity count carefully

    Workarounds: - Use object pooling - Reuse entities - Disable entities instead of removing - Combine multiple entities into one

    "},{"location":"resources/limitations_and_considerations/#no-rtti-runtime-type-information","title":"No RTTI (Runtime Type Information)","text":"

    Impact: - Cannot use dynamic_cast in most code - Type checking must be done manually - Limits polymorphism patterns

    Alternatives: - Use virtual functions - Manual type checking - Tag-based systems

    "},{"location":"resources/limitations_and_considerations/#no-exceptions-in-critical-code","title":"No Exceptions in Critical Code","text":"

    Impact: - Cannot use try/catch in game loop - Error handling must be explicit - Crashes instead of exceptions

    Best Practices: - Validate inputs - Check return values - Use assertions for debugging - Handle errors explicitly

    "},{"location":"resources/limitations_and_considerations/#no-dynamic-allocation-in-game-loop","title":"No Dynamic Allocation in Game Loop","text":"

    Impact: - Cannot use new/delete during gameplay - Must pre-allocate resources - Limits flexibility

    Solutions: - Object pooling - Pre-allocation in init() - Static buffers - Fixed-size arrays

    "},{"location":"resources/limitations_and_considerations/#no-advanced-features","title":"No Advanced Features","text":"

    Not Supported: - 3D graphics - Shaders - Advanced physics (joints, constraints) - Networking - File system (ESP32) - Advanced audio effects

    Focus: - 2D sprite-based games - Simple physics - Retro-style games - Embedded-friendly features

    "},{"location":"resources/limitations_and_considerations/#experimental-features","title":"Experimental Features","text":""},{"location":"resources/limitations_and_considerations/#2bpp-sprites","title":"2bpp Sprites","text":"

    Status: Experimental - Requires PIXELROOT32_ENABLE_2BPP_SPRITES flag - May have bugs or limitations - Not fully tested

    Use with caution: - Test thoroughly - May change in future versions - Report issues if found

    "},{"location":"resources/limitations_and_considerations/#4bpp-sprites","title":"4bpp Sprites","text":"

    Status: Experimental - Requires PIXELROOT32_ENABLE_4BPP_SPRITES flag - More experimental than 2bpp - Higher memory usage

    Use with caution: - Test extensively - Monitor memory usage - May be unstable

    "},{"location":"resources/limitations_and_considerations/#scene-arena","title":"Scene Arena","text":"

    Status: Experimental - Requires PIXELROOT32_ENABLE_SCENE_ARENA flag - Alternative memory management - May have bugs

    Recommendations: - Use object pooling instead (more stable) - Test thoroughly if using - May be removed or changed

    "},{"location":"resources/limitations_and_considerations/#unsupported-features-current","title":"Unsupported Features (Current)","text":""},{"location":"resources/limitations_and_considerations/#planned-but-not-available","title":"Planned but Not Available","text":"
    • u8g2 Driver: Alternative display driver (planned)
    • Music Compiler: Tool to convert music files (planned)
    • Tilemap Compiler: Tool to create tilemaps (planned)
    • Save System: Persistent storage system (planned)
    • Spatial Partitioning: Advanced collision optimization (planned)
    "},{"location":"resources/limitations_and_considerations/#not-planned","title":"Not Planned","text":"
    • 3D Graphics: 2D-only engine
    • Networking: No network support
    • File System: No file I/O on ESP32
    • Advanced Audio: NES-like audio only
    • Scripting: No Lua/JavaScript support
    "},{"location":"resources/limitations_and_considerations/#best-practices-for-esp32","title":"Best Practices for ESP32","text":""},{"location":"resources/limitations_and_considerations/#memory-management","title":"Memory Management","text":"
    • Pre-allocate: All resources in init()
    • Object pooling: Reuse entities
    • Flash storage: Use const/constexpr for data
    • Avoid strings: Use static buffers
    • Monitor usage: Check heap regularly
    "},{"location":"resources/limitations_and_considerations/#performance","title":"Performance","text":"
    • Limit entities: Stay well below MAX_ENTITIES
    • Optimize rendering: Use culling, batching
    • Cache calculations: Avoid repeated work
    • Profile on hardware: PC performance \u2260 ESP32
    "},{"location":"resources/limitations_and_considerations/#development","title":"Development","text":"
    • Test on hardware: Don't rely only on Native
    • Start simple: Add complexity gradually
    • Monitor memory: Watch for leaks
    • Optimize incrementally: Profile and optimize
    "},{"location":"resources/limitations_and_considerations/#what-pixelroot32-is-good-for","title":"What PixelRoot32 IS Good For","text":"

    \u2705 Retro-style 2D games \u2705 Arcade games \u2705 Puzzle games \u2705 Platformers \u2705 Shooters \u2705 Educational projects \u2705 Prototyping \u2705 Embedded game development

    "},{"location":"resources/limitations_and_considerations/#what-pixelroot32-is-not-good-for","title":"What PixelRoot32 is NOT Good For","text":"

    \u274c 3D games \u274c Complex physics simulations \u274c Large open worlds \u274c Games requiring many entities \u274c Games with complex graphics \u274c Network multiplayer \u274c Games requiring file I/O

    "},{"location":"resources/limitations_and_considerations/#making-informed-decisions","title":"Making Informed Decisions","text":""},{"location":"resources/limitations_and_considerations/#before-starting-a-project","title":"Before Starting a Project","text":"
    1. Assess requirements: Does PixelRoot32 fit?
    2. Check limitations: Can you work within constraints?
    3. Plan architecture: Design around limitations
    4. Test early: Verify on hardware early
    "},{"location":"resources/limitations_and_considerations/#if-limitations-are-a-problem","title":"If Limitations Are a Problem","text":"

    Consider alternatives: - Full game engines (Unity, Godot) for complex games - Custom solutions for specific needs - Different hardware for more resources

    Or work within limits: - Simplify game design - Optimize aggressively - Use creative solutions

    "},{"location":"resources/limitations_and_considerations/#version-compatibility","title":"Version Compatibility","text":""},{"location":"resources/limitations_and_considerations/#current-version","title":"Current Version","text":"
    • Engine Version: 0.2.0-dev
    • API Stability: May change
    • Breaking Changes: Possible in future versions

    Recommendations: - Pin exact version in platformio.ini - Don't use ^ or fuzzy versioning - Test after engine updates - Review changelog

    "},{"location":"resources/limitations_and_considerations/#honest-assessment","title":"Honest Assessment","text":"

    PixelRoot32 is designed for: - Simple to medium complexity games - Retro/arcade style - ESP32 hardware constraints - Rapid development

    It is not designed for: - AAA game complexity - Modern graphics - Large-scale games - Unlimited resources

    "},{"location":"resources/limitations_and_considerations/#see-also","title":"See Also","text":"
    • Memory Management - Working with memory limits
    • Performance Tuning - Optimizing performance
    • Troubleshooting - Solving problems
    • FAQ - Common questions

    Remember: Understanding limitations helps you build better games within PixelRoot32's capabilities.

    "},{"location":"resources/troubleshooting/","title":"Troubleshooting","text":"

    This guide helps you diagnose and fix common issues when developing with PixelRoot32.

    "},{"location":"resources/troubleshooting/#compilation-problems","title":"Compilation Problems","text":""},{"location":"resources/troubleshooting/#common-compilation-errors","title":"Common Compilation Errors","text":"

    Error: Library not found

    Solution: Ensure PixelRoot32-Game-Engine is properly installed\n- Check platformio.ini has correct lib_deps\n- Verify library version matches (use exact version, not ^)\n- Try: pio lib install\n

    Error: Include file not found

    Solution: Check include paths\n- Verify lib_extra_dirs in platformio.ini\n- Check that library is in lib/ directory\n- Ensure correct namespace (pixelroot32::)\n

    Error: Undefined reference

    Solution: Link missing libraries\n- Check all required libraries are listed\n- Verify TFT_eSPI is installed for ESP32\n- Check SDL2 is installed for Native builds\n

    Error: Build flags not recognized

    Solution: Verify build flag syntax\n- Use -D FLAG_NAME (not --define)\n- Check flag names are correct\n- Ensure flags are in correct environment section\n

    "},{"location":"resources/troubleshooting/#configuration-issues","title":"Configuration Issues","text":"

    Wrong display type: - Verify DisplayType matches your hardware - Check TFT_eSPI build flags match display - Test with different display types

    Incorrect pin configuration: - Verify GPIO pins match your wiring - Check pin numbers in platformio.ini - Ensure pins aren't used by other peripherals

    "},{"location":"resources/troubleshooting/#hardware-problems-esp32","title":"Hardware Problems (ESP32)","text":""},{"location":"resources/troubleshooting/#display-not-working","title":"Display Not Working","text":"

    Symptoms: - Blank screen - Garbled display - No output

    Solutions: 1. Check wiring: - Verify SPI connections (MOSI, SCLK, DC, RST) - Check power supply (3.3V or 5V as required) - Ensure ground connections

    1. Verify configuration:
    2. Check display type matches hardware
    3. Verify pin numbers in platformio.ini
    4. Test with known working configuration

    5. SPI frequency:

    6. Lower SPI frequency (try 20MHz instead of 40MHz)
    7. Some displays need slower speeds
    8. Check display datasheet for max frequency

    9. Display initialization:

    10. Try different rotation values
    11. Check display width/height settings
    12. Verify TFT_eSPI driver is correct
    "},{"location":"resources/troubleshooting/#buttons-not-responding","title":"Buttons Not Responding","text":"

    Symptoms: - No input detected - Buttons don't trigger actions - Input feels laggy

    Solutions: 1. Check wiring: - Verify button connections to GPIO pins - Check pull-up/pull-down resistors - Test buttons with multimeter

    1. Verify pin configuration:
    2. Check InputConfig pin numbers
    3. Ensure pins match hardware
    4. Verify pins aren't used elsewhere

    5. Input debouncing:

    6. Add hardware debouncing (capacitor)
    7. Check InputManager is being updated
    8. Verify input is read in update(), not draw()

    9. Button logic:

    10. Test with isButtonDown() vs isButtonPressed()
    11. Check button indices match configuration
    12. Verify input is accessed correctly
    "},{"location":"resources/troubleshooting/#audio-not-working","title":"Audio Not Working","text":"

    Symptoms: - No sound output - Distorted audio - Audio glitches

    Solutions: 1. DAC Configuration: - Verify DAC pin (25 or 26 for ESP32) - Check sample rate (11025 Hz recommended) - Ensure audio backend is initialized

    1. I2S Configuration:
    2. Verify I2S pin connections (BCLK, LRCK, DOUT)
    3. Check external DAC is powered
    4. Verify I2S DAC is compatible

    5. Audio quality:

    6. Lower sample rate if distorted
    7. Reduce volume levels
    8. Check for too many simultaneous sounds
    9. Verify audio buffer size

    10. Hardware:

    11. Check speaker connections
    12. Verify amplifier is powered
    13. Test with different audio hardware
    14. Check audio cable connections
    "},{"location":"resources/troubleshooting/#power-issues","title":"Power Issues","text":"

    Symptoms: - ESP32 resets randomly - Display flickers - Unstable operation

    Solutions: 1. Power supply: - Use adequate power supply (500mA+ recommended) - Check voltage is stable (3.3V) - Add decoupling capacitors

    1. Current draw:
    2. Display draws significant current
    3. Audio amplifier adds load
    4. Reduce brightness if possible

    5. Wiring:

    6. Use thick wires for power
    7. Keep power wires short
    8. Add capacitors near ESP32
    "},{"location":"resources/troubleshooting/#performance-problems","title":"Performance Problems","text":""},{"location":"resources/troubleshooting/#low-fps","title":"Low FPS","text":"

    Symptoms: - Game runs slowly - Laggy movement - Stuttering

    Solutions: 1. Reduce entity count: - Limit active entities (MAX_ENTITIES = 32) - Disable off-screen entities - Use object pooling

    1. Optimize rendering:
    2. Use viewport culling
    3. Reduce draw calls
    4. Use tilemaps instead of individual sprites
    5. Limit sprite count

    6. Simplify logic:

    7. Cache expensive calculations
    8. Reduce collision checks
    9. Lower update frequency for non-critical entities

    10. Check hardware:

    11. Verify ESP32 is running at full speed (240MHz)
    12. Check for thermal throttling
    13. Ensure adequate power supply
    "},{"location":"resources/troubleshooting/#frame-drops","title":"Frame Drops","text":"

    Symptoms: - Occasional stuttering - Inconsistent frame times - Periodic freezes

    Solutions: 1. Identify bottlenecks: - Profile frame time - Check for expensive operations - Look for blocking code

    1. Optimize update loop:
    2. Avoid dynamic allocation
    3. Cache calculations
    4. Reduce string operations

    5. Memory issues:

    6. Check for memory leaks
    7. Reduce memory usage
    8. Use object pooling
    "},{"location":"resources/troubleshooting/#freezescrashes","title":"Freezes/Crashes","text":"

    Symptoms: - Game stops responding - ESP32 resets - Watchdog resets

    Solutions: 1. Memory issues: - Check available heap memory - Reduce entity count - Avoid dynamic allocation - Use object pooling

    1. Infinite loops:
    2. Check for infinite loops in update()
    3. Verify collision detection doesn't loop
    4. Check animation logic

    5. Stack overflow:

    6. Avoid large stack allocations
    7. Reduce recursion depth
    8. Move large data to heap (carefully)

    9. Watchdog:

    10. Ensure update() completes quickly
    11. Add yield() calls if needed
    12. Check for blocking operations
    "},{"location":"resources/troubleshooting/#memory-problems","title":"Memory Problems","text":""},{"location":"resources/troubleshooting/#out-of-memory","title":"Out of Memory","text":"

    Symptoms: - Compilation fails - Runtime crashes - \"Allocation failed\" errors

    Solutions: 1. Reduce memory usage: - Use 1bpp sprites instead of 2bpp/4bpp - Store data in flash (const/constexpr) - Reduce entity count - Use object pooling

    1. Optimize data:
    2. Reuse sprites
    3. Compress tilemap data
    4. Remove unused code/data

    5. Memory management:

    6. Avoid dynamic allocation in game loop
    7. Pre-allocate all resources
    8. Use static buffers
    "},{"location":"resources/troubleshooting/#memory-fragmentation","title":"Memory Fragmentation","text":"

    Symptoms: - Gradual performance degradation - Allocation failures over time - Crashes after running for a while

    Solutions: 1. Use object pooling: - Pre-allocate entities - Reuse objects instead of creating/destroying - Avoid frequent new/delete

    1. Pre-allocate resources:
    2. Allocate everything in init()
    3. Use fixed-size arrays
    4. Avoid dynamic containers
    "},{"location":"resources/troubleshooting/#native-build-problems","title":"Native Build Problems","text":""},{"location":"resources/troubleshooting/#sdl2-not-found","title":"SDL2 Not Found","text":"

    Symptoms: - Compilation fails - Linker errors - Missing SDL2 symbols

    Solutions: 1. Install SDL2: - Windows (MSYS2): pacman -S mingw-w64-x86_64-SDL2 - Linux: sudo apt-get install libsdl2-dev - macOS: brew install sdl2

    1. Check paths:
    2. Verify include paths in platformio.ini
    3. Check library paths
    4. Ensure SDL2 version is compatible

    5. Linker flags:

    6. Verify -lSDL2 is in build flags
    7. Check library search paths
    8. Ensure SDL2 DLL is accessible (Windows)
    "},{"location":"resources/troubleshooting/#window-not-opening","title":"Window Not Opening","text":"

    Symptoms: - Program runs but no window - Console shows errors - Immediate exit

    Solutions: 1. Check SDL2 initialization: - Verify SDL2 is properly initialized - Check for SDL2 error messages - Ensure display config is correct

    1. Graphics drivers:
    2. Update graphics drivers
    3. Check SDL2 video backend
    4. Test with simple SDL2 program

    5. Console output:

    6. Run from terminal to see errors
    7. Check for error messages
    8. Verify SDL2 is working
    "},{"location":"resources/troubleshooting/#debugging-techniques","title":"Debugging Techniques","text":""},{"location":"resources/troubleshooting/#serial-debugging-esp32","title":"Serial Debugging (ESP32)","text":"
    void setup() {\n    Serial.begin(115200);\n    // ... initialization\n\n    Serial.println(\"Engine initialized\");\n}\n\nvoid update(unsigned long deltaTime) override {\n    // Debug output\n    if (frameCount % 60 == 0) {\n        Serial.print(\"FPS: \");\n        Serial.println(1000.0f / deltaTime);\n    }\n    frameCount++;\n}\n
    "},{"location":"resources/troubleshooting/#memory-monitoring","title":"Memory Monitoring","text":"
    #ifdef PLATFORM_ESP32\n#include <Arduino.h>\n\nvoid checkMemory() {\n    Serial.print(\"Free heap: \");\n    Serial.println(ESP.getFreeHeap());\n    Serial.print(\"Largest block: \");\n    Serial.println(ESP.getMaxAllocHeap());\n}\n#endif\n
    "},{"location":"resources/troubleshooting/#performance-profiling","title":"Performance Profiling","text":"
    class Profiler {\nprivate:\n    unsigned long updateTime = 0;\n    unsigned long drawTime = 0;\n\npublic:\n    void startUpdate() {\n        updateTime = micros();\n    }\n\n    void endUpdate() {\n        updateTime = micros() - updateTime;\n    }\n\n    void log() {\n        Serial.print(\"Update: \");\n        Serial.print(updateTime);\n        Serial.print(\"us, Draw: \");\n        Serial.println(drawTime);\n    }\n};\n
    "},{"location":"resources/troubleshooting/#visual-debugging","title":"Visual Debugging","text":"
    • Draw hitboxes: Visualize collision boxes
    • Show FPS: Display frame rate on screen
    • Entity count: Show active entity count
    • Memory usage: Display memory statistics
    "},{"location":"resources/troubleshooting/#common-patterns-for-debugging","title":"Common Patterns for Debugging","text":""},{"location":"resources/troubleshooting/#isolate-the-problem","title":"Isolate the Problem","text":"
    1. Minimal reproduction: Create smallest code that shows issue
    2. Disable features: Turn off systems one by one
    3. Test incrementally: Add features back one at a time
    "},{"location":"resources/troubleshooting/#check-the-basics","title":"Check the Basics","text":"
    1. Verify initialization: Ensure engine.init() is called
    2. Check scene setup: Verify scene is set and initialized
    3. Test on both platforms: Compare ESP32 vs Native behavior
    4. Review recent changes: What changed before the issue?
    "},{"location":"resources/troubleshooting/#use-logging","title":"Use Logging","text":"
    #define DEBUG_MODE\n\n#ifdef DEBUG_MODE\n    #define DEBUG_LOG(x) Serial.println(x)\n#else\n    #define DEBUG_LOG(x)\n#endif\n\n// Usage\nDEBUG_LOG(\"Entity created\");\nDEBUG_LOG(\"Collision detected\");\n
    "},{"location":"resources/troubleshooting/#getting-help","title":"Getting Help","text":"

    If you can't resolve an issue:

    1. Check documentation: Review relevant guides
    2. Search examples: Look at example games
    3. Review code: Check engine source code
    4. Community: Ask on Discord or GitHub
    5. Report issue: Create detailed bug report
    "},{"location":"resources/troubleshooting/#bug-report-template","title":"Bug Report Template","text":"

    When reporting issues, include:

    • Platform: ESP32 or Native
    • Hardware: Display type, ESP32 model
    • Code: Minimal reproduction code
    • Error messages: Full error output
    • Expected behavior: What should happen
    • Actual behavior: What actually happens
    • Steps to reproduce: How to trigger the issue
    "},{"location":"resources/troubleshooting/#see-also","title":"See Also","text":"
    • Limitations and Considerations - Known limitations
    • Performance Tuning - Performance optimization
    • Memory Management - Memory optimization
    • FAQ - Frequently asked questions

    Note: Many issues are configuration-related. Double-check your setup before assuming a bug.

    "},{"location":"tools/sprite_compiler/advanced_features/","title":"Sprite Compiler Advanced Features","text":"

    Advanced features and options for the PixelRoot32 Sprite Compiler to optimize sprite conversion and handle complex scenarios.

    "},{"location":"tools/sprite_compiler/advanced_features/#automatic-palette-detection","title":"Automatic Palette Detection","text":"

    The Sprite Compiler automatically detects if your sprite uses one of the engine's built-in palettes. This simplifies your workflow and ensures color consistency.

    "},{"location":"tools/sprite_compiler/advanced_features/#predefined-engine-palettes","title":"Predefined Engine Palettes","text":"

    The engine includes 5 optimized palettes: - PR32 (Default PixelRoot32 palette) - NES (Nintendo style) - GB (GameBoy style) - GBC (GameBoy Color style) - PICO8 (Fantasy console style)

    "},{"location":"tools/sprite_compiler/advanced_features/#how-it-works","title":"How it works","text":"
    1. Detection: When you compile an image, the tool compares all unique colors found in the sprite with the colors in the 5 engine palettes.
    2. Match: If all colors in your sprite belong to one of these palettes, the compiler:
    3. Omits generating a color array in the header.
    4. Assumes you will use the engine's built-in palette definitions at runtime.
    5. Custom Palette: If your sprite uses colors not found in the engine palettes, it automatically generates a {PREFIX}_PALETTE_MAPPING[16] array in the header file.
    "},{"location":"tools/sprite_compiler/advanced_features/#naming-with-prefixes","title":"Naming with Prefixes","text":"

    You can organize your generated code by using the --prefix parameter (or the Prefix field in the GUI).

    "},{"location":"tools/sprite_compiler/advanced_features/#using-prefixes","title":"Using Prefixes","text":"

    By default, sprites are named SPRITE_N_.... Using a prefix allows you to create more descriptive names and avoid naming collisions.

    python main.py sheet.png --grid 16x16 --sprite 0,0,1,1 --prefix PLAYER_JUM\n

    Generated names will follow this pattern: - PLAYER_JUM_SPRITE_0_LAYER_0 - PLAYER_JUM_SPRITE_0_2BPP - PLAYER_JUM_SPRITE_0_4BPP - PLAYER_JUM_PALETTE_MAPPING (if a custom palette is used)

    "},{"location":"tools/sprite_compiler/advanced_features/#export-modes","title":"Export Modes","text":""},{"location":"tools/sprite_compiler/advanced_features/#layered-1bpp","title":"Layered (1bpp)","text":"

    Best for standard PixelRoot32 rendering. It extracts each color into its own bitmask (1bpp). The engine then renders these layers sequentially.

    "},{"location":"tools/sprite_compiler/advanced_features/#packed-2bpp-4bpp","title":"Packed (2bpp / 4bpp)","text":"

    Generates a single packed array where each pixel uses multiple bits. - 2bpp: 4 colors max (Index 0 is always transparent). - 4bpp: 16 colors max (Index 0 is always transparent).

    These modes are more efficient for sprites with many colors as they require only a single draw call.

    "},{"location":"tools/sprite_compiler/installation/","title":"Sprite Compiler Installation","text":"

    This guide walks you through installing the PixelRoot32 Sprite Compiler on your system.

    "},{"location":"tools/sprite_compiler/installation/#prerequisites","title":"Prerequisites","text":""},{"location":"tools/sprite_compiler/installation/#required-software","title":"Required Software","text":"
    • Python: Version 3.8 or higher
    • pip: Usually included with Python
    "},{"location":"tools/sprite_compiler/installation/#verify-prerequisites","title":"Verify Prerequisites","text":"

    Check if Python is installed:

    python --version\n# Should show 3.8.0 or higher\n

    Check if pip is installed:

    pip --version\n# Should show version number\n

    If not installed, download from python.org

    "},{"location":"tools/sprite_compiler/installation/#installation-methods","title":"Installation Methods","text":""},{"location":"tools/sprite_compiler/installation/#method-1-from-source-recommended","title":"Method 1: From Source (Recommended)","text":""},{"location":"tools/sprite_compiler/installation/#step-1-clone-repository","title":"Step 1: Clone Repository","text":"
    git clone https://github.com/Gperez88/PixelRoot32-Sprite-Compiler.git\ncd PixelRoot32-Sprite-Compiler\n
    "},{"location":"tools/sprite_compiler/installation/#step-2-install-dependencies","title":"Step 2: Install Dependencies","text":"
    pip install -r requirements.txt\n
    "},{"location":"tools/sprite_compiler/installation/#step-3-verify-installation","title":"Step 3: Verify Installation","text":"
    python main.py --help\n
    "},{"location":"tools/sprite_compiler/installation/#method-2-standalone-executable-windows","title":"Method 2: Standalone Executable (Windows)","text":"

    If you are on Windows, you can download the latest standalone .exe from the Releases section of the repository. This does not require Python or any dependencies to be installed.

    "},{"location":"tools/sprite_compiler/installation/#platform-specific-instructions","title":"Platform-Specific Instructions","text":""},{"location":"tools/sprite_compiler/installation/#windows","title":"Windows","text":""},{"location":"tools/sprite_compiler/installation/#using-npm-recommended","title":"Using npm (Recommended)","text":"
    1. Install Node.js from nodejs.org
    2. Download the Windows installer
    3. Run the installer
    4. Restart your terminal/command prompt

    5. Open Command Prompt or PowerShell

    6. Install globally:

      npm install -g pr32-sprite-compiler\n

    7. Verify:

      pr32-sprite-compiler --version\n

    "},{"location":"tools/sprite_compiler/installation/#troubleshooting-windows-issues","title":"Troubleshooting Windows Issues","text":"

    \"pr32-sprite-compiler is not recognized\": - Ensure Node.js is in your PATH - Restart terminal after installation - Try using full path: C:\\Users\\YourName\\AppData\\Roaming\\npm\\pr32-sprite-compiler.cmd

    Permission errors: - Run terminal as Administrator - Or install locally: npm install pr32-sprite-compiler (without -g)

    "},{"location":"tools/sprite_compiler/installation/#linux","title":"Linux","text":""},{"location":"tools/sprite_compiler/installation/#using-npm","title":"Using npm","text":"
    1. Install Node.js (if not already installed):

    Ubuntu/Debian:

    curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -\nsudo apt-get install -y nodejs\n

    Fedora/RHEL:

    sudo dnf install nodejs npm\n

    1. Install Sprite Compiler:

      sudo npm install -g pr32-sprite-compiler\n

    2. Verify:

      pr32-sprite-compiler --version\n

    "},{"location":"tools/sprite_compiler/installation/#troubleshooting-linux-issues","title":"Troubleshooting Linux Issues","text":"

    Permission denied: - Use sudo for global installation - Or install locally without -g flag

    Command not found: - Check npm global bin path: npm config get prefix - Add to PATH if needed: export PATH=$PATH:$(npm config get prefix)/bin

    "},{"location":"tools/sprite_compiler/installation/#macos","title":"macOS","text":""},{"location":"tools/sprite_compiler/installation/#using-npm_1","title":"Using npm","text":"
    1. Install Node.js:
    2. Download from nodejs.org
    3. Or use Homebrew: brew install node

    4. Install Sprite Compiler:

      npm install -g pr32-sprite-compiler\n

    5. Verify:

      pr32-sprite-compiler --version\n

    "},{"location":"tools/sprite_compiler/installation/#using-homebrew-alternative","title":"Using Homebrew (Alternative)","text":"

    If available as a Homebrew formula:

    brew install pr32-sprite-compiler\n

    "},{"location":"tools/sprite_compiler/installation/#gui-version-installation","title":"GUI Version Installation","text":"

    If a GUI version is available:

    "},{"location":"tools/sprite_compiler/installation/#windows_1","title":"Windows","text":"

    Download the installer from the releases page and run it.

    "},{"location":"tools/sprite_compiler/installation/#linux_1","title":"Linux","text":"
    # Download AppImage or .deb package\n# Make executable and run\nchmod +x pr32-sprite-compiler-gui.AppImage\n./pr32-sprite-compiler-gui.AppImage\n
    "},{"location":"tools/sprite_compiler/installation/#macos_1","title":"macOS","text":"

    Download the .dmg file from releases and install.

    "},{"location":"tools/sprite_compiler/installation/#verification","title":"Verification","text":"

    After installation, verify everything works:

    "},{"location":"tools/sprite_compiler/installation/#test-basic-functionality","title":"Test Basic Functionality","text":"
    1. Create a test image:
    2. Create an 8x8 pixel PNG image (black and white)
    3. Save as test.png

    4. Run compiler:

      pr32-sprite-compiler test.png test_output.h\n

    5. Check output:

    6. File test_output.h should be created
    7. Should contain sprite data arrays
    "},{"location":"tools/sprite_compiler/installation/#check-version","title":"Check Version","text":"
    pr32-sprite-compiler --version\n
    "},{"location":"tools/sprite_compiler/installation/#check-help","title":"Check Help","text":"
    pr32-sprite-compiler --help\n
    "},{"location":"tools/sprite_compiler/installation/#updating","title":"Updating","text":""},{"location":"tools/sprite_compiler/installation/#update-via-npm","title":"Update via npm","text":"
    npm update -g pr32-sprite-compiler\n
    "},{"location":"tools/sprite_compiler/installation/#update-from-source","title":"Update from Source","text":"
    cd pr32-sprite-compiler\ngit pull\nnpm install\n
    "},{"location":"tools/sprite_compiler/installation/#uninstallation","title":"Uninstallation","text":""},{"location":"tools/sprite_compiler/installation/#remove-global-installation","title":"Remove Global Installation","text":"
    npm uninstall -g pr32-sprite-compiler\n
    "},{"location":"tools/sprite_compiler/installation/#remove-local-installation","title":"Remove Local Installation","text":"
    npm uninstall pr32-sprite-compiler\n
    "},{"location":"tools/sprite_compiler/installation/#troubleshooting","title":"Troubleshooting","text":""},{"location":"tools/sprite_compiler/installation/#common-issues","title":"Common Issues","text":"

    \"Command not found\" after installation: - Restart your terminal - Check npm global bin path: npm config get prefix - Verify PATH includes npm bin directory

    Permission errors: - On Linux/macOS: Use sudo for global install - Or install locally without -g flag - On Windows: Run terminal as Administrator

    Module not found errors: - Reinstall: npm install -g pr32-sprite-compiler - Clear npm cache: npm cache clean --force

    Version conflicts: - Check Node.js version: node --version - Update Node.js if version is too old - Use nvm (Node Version Manager) to manage versions

    "},{"location":"tools/sprite_compiler/installation/#getting-help","title":"Getting Help","text":"
    • Check the Usage Guide for usage examples
    • Review Troubleshooting for common issues
    • Open an issue on GitHub if problems persist
    "},{"location":"tools/sprite_compiler/installation/#next-steps","title":"Next Steps","text":"

    Once installed, proceed to: - Usage Guide - Learn how to use the compiler - Advanced Features - Explore advanced options

    "},{"location":"tools/sprite_compiler/installation/#see-also","title":"See Also","text":"
    • Overview - What the Sprite Compiler does
    • Available Tools - All PixelRoot32 tools
    "},{"location":"tools/sprite_compiler/overview/","title":"Sprite Compiler Overview","text":"

    The Sprite Compiler is a tool that converts PNG images into PixelRoot32 sprite data formats. It provides both a graphical interface (GUI) and a command-line interface (CLI) to automate the process of creating sprite arrays from image files.

    "},{"location":"tools/sprite_compiler/overview/#what-it-does","title":"What It Does","text":"

    The Sprite Compiler takes bitmap images (PNG) and converts them into C header files containing:

    • Sprite data arrays: Optimized uint16_t arrays for various formats.
    • Layered support: Generates multiple 1bpp layers for complex sprites.
    • Packed formats: Supports 2bpp and 4bpp packed formats.
    • Sprite sheets: Handles grid-based sprite sheets with auto-detection.
    "},{"location":"tools/sprite_compiler/overview/#key-features","title":"Key Features","text":""},{"location":"tools/sprite_compiler/overview/#format-support","title":"\u2705 Format Support","text":"
    • Layered (1bpp): Standard format, generates one array per color.
    • 2bpp (4 colors): Packed format, 2 bits per pixel.
    • 4bpp (16 colors): Packed format, 4 bits per pixel.
    "},{"location":"tools/sprite_compiler/overview/#gui-cli","title":"\u2705 GUI & CLI","text":"
    • Modern GUI: Step-by-step card-based interface for easy configuration.
    • Powerful CLI: Perfect for build scripts and automation.
    "},{"location":"tools/sprite_compiler/overview/#sprite-sheets","title":"\u2705 Sprite Sheets","text":"

    Automatically split sprite sheets into individual sprites:

    python main.py sheet.png --grid 16x16 --sprite 0,0,1,1 --sprite 1,0,1,1 --out output.h\n

    "},{"location":"tools/sprite_compiler/overview/#gui-interface","title":"GUI Interface","text":"

    The GUI is designed to be intuitive and follows a 5-step process:

    1. Input Image: Select your PNG source.
    2. Grid Settings: Define the cell size and offsets.
    3. Sprite Selection: Pick which cells to export.
    4. Export Settings: Choose the mode (Layered, 2bpp, 4bpp), set a Prefix, and choose the output path.
    5. About: Quick access to version info and credits (via the ? button).
    6. Log: Technical feedback and performance alerts.
    "},{"location":"tools/sprite_compiler/overview/#input-requirements","title":"Input Requirements","text":""},{"location":"tools/sprite_compiler/overview/#supported-formats","title":"Supported Formats","text":"
    • PNG: Primary format (recommended)
    • Indexed color PNG: Best for 1bpp conversion
    • Grayscale PNG: Automatically converted to 1bpp
    • RGB PNG: Converted using threshold or palette
    "},{"location":"tools/sprite_compiler/overview/#image-constraints","title":"Image Constraints","text":"

    For 1bpp sprites: - Maximum width: 16 pixels - Height: Any (typically 8, 16, 32 pixels) - Colors: Black and white (or converted automatically)

    For 2bpp sprites: - Maximum width: 16 pixels - Colors: Up to 4 colors

    For 4bpp sprites: - Maximum width: 16 pixels - Colors: Up to 16 colors

    "},{"location":"tools/sprite_compiler/overview/#output-format","title":"Output Format","text":"

    The compiler generates C header files with optimized arrays:

    // Generated by PixelRoot32 Sprite Compiler\n\n// Optional palette mapping if using custom colors\nstatic const Color PLAYER_PALETTE_MAPPING[16] = {\n    (Color)0, (Color)1, (Color)2, (Color)3,\n    // ...\n};\n\n// Sprite data array (4bpp example)\nstatic const uint16_t PLAYER_SPRITE_0_4BPP[] = {\n    0x0000, 0x1234, 0x5678, // Row 0\n    // ... more rows\n};\n
    "},{"location":"tools/sprite_compiler/overview/#use-cases","title":"Use Cases","text":""},{"location":"tools/sprite_compiler/overview/#1-single-sprite-conversion","title":"1. Single Sprite Conversion","text":"

    Convert a single image to a sprite:

    pr32-sprite-compiler player.png player_sprite.h --name PLAYER_SPRITE\n

    "},{"location":"tools/sprite_compiler/overview/#2-animation-frames","title":"2. Animation Frames","text":"

    Convert multiple frames for animation:

    pr32-sprite-compiler --batch walk_*.png --output-dir animations/ --prefix WALK_\n

    "},{"location":"tools/sprite_compiler/overview/#3-sprite-sheet-processing","title":"3. Sprite Sheet Processing","text":"

    Split a sprite sheet into individual sprites:

    pr32-sprite-compiler characters.png output.h --sheet 16x16 --count 8\n

    "},{"location":"tools/sprite_compiler/overview/#4-batch-asset-processing","title":"4. Batch Asset Processing","text":"

    Process entire asset directories:

    pr32-sprite-compiler --batch assets/sprites/*.png --output-dir src/sprites/\n

    "},{"location":"tools/sprite_compiler/overview/#workflow-integration","title":"Workflow Integration","text":""},{"location":"tools/sprite_compiler/overview/#typical-development-workflow","title":"Typical Development Workflow","text":"
    1. Create sprites in your image editor (Aseprite, Piskel, GIMP, etc.)
    2. Save as PNG with appropriate dimensions
    3. Run compiler to generate header files
    4. Include headers in your PixelRoot32 project
    5. Use sprites in your game code
    "},{"location":"tools/sprite_compiler/overview/#automation-example","title":"Automation Example","text":"
    #!/bin/bash\n# build-sprites.sh\n\n# Compile all sprites\npr32-sprite-compiler assets/sprites/*.png --output-dir src/sprites/\n\n# Continue with your build process\nplatformio run\n
    "},{"location":"tools/sprite_compiler/overview/#advantages-over-manual-creation","title":"Advantages Over Manual Creation","text":""},{"location":"tools/sprite_compiler/overview/#time-saving","title":"\u2705 Time Saving","text":"
    • No manual bit pattern conversion
    • Automatic format optimization
    • Batch processing multiple sprites
    "},{"location":"tools/sprite_compiler/overview/#accuracy","title":"\u2705 Accuracy","text":"
    • Correct bit ordering
    • Proper array formatting
    • Valid C++ syntax
    "},{"location":"tools/sprite_compiler/overview/#consistency","title":"\u2705 Consistency","text":"
    • Uniform naming conventions
    • Standardized output format
    • Consistent code structure
    "},{"location":"tools/sprite_compiler/overview/#maintainability","title":"\u2705 Maintainability","text":"
    • Easy to regenerate from source images
    • Version control friendly
    • Clear separation of assets and code
    "},{"location":"tools/sprite_compiler/overview/#limitations","title":"Limitations","text":"
    • Width limit: 16 pixels for 1bpp (hardware constraint)
    • Color depth: Limited by format (1bpp = 2 colors, 2bpp = 4 colors, etc.)
    • File format: Primarily PNG (other formats may require conversion)
    "},{"location":"tools/sprite_compiler/overview/#next-steps","title":"Next Steps","text":"
    • Installation Guide - Set up the compiler
    • Usage Guide - Learn how to use it
    • Advanced Features - Explore advanced options
    "},{"location":"tools/sprite_compiler/overview/#see-also","title":"See Also","text":"
    • Manual - Sprites and Animation - Using sprites in games
    • Code Examples - Sprites - Sprite usage examples
    • Available Tools - All PixelRoot32 tools
    "},{"location":"tools/sprite_compiler/usage_guide/","title":"Sprite Compiler Usage Guide","text":"

    Complete guide to using the PixelRoot32 Sprite Compiler for converting images to sprite data.

    "},{"location":"tools/sprite_compiler/usage_guide/#basic-usage","title":"Basic Usage","text":""},{"location":"tools/sprite_compiler/usage_guide/#launching-the-gui","title":"Launching the GUI","text":"

    The easiest way to use the compiler is via the Graphical User Interface (GUI).

    python main.py\n

    This will open the application where you can interactively load images, configure the grid, and export sprites.

    "},{"location":"tools/sprite_compiler/usage_guide/#command-line-interface-cli","title":"Command Line Interface (CLI)","text":"

    For automation, you can use the CLI mode by passing arguments to the script.

    python main.py [input] [options]\n

    Required:

    • input: Input PNG image file
    • --grid WxH: Grid cell size (e.g., 16x16)
    • --sprite gx,gy,gw,gh: Sprite definition (can be repeated)

    Optional:

    • --prefix NAME: Prefix for generated arrays (e.g., PLAYER_JUM)
    • --out FILE: Output header file (default: sprites.h)
    • --offset X,Y: Initial offset in pixels (default: 0,0)
    • --mode MODE: Export mode (layered, 2bpp, 4bpp)
    "},{"location":"tools/sprite_compiler/usage_guide/#cli-examples","title":"CLI Examples","text":""},{"location":"tools/sprite_compiler/usage_guide/#simple-conversion","title":"Simple Conversion","text":"

    Convert a single 16x16 sprite located at the top-left corner:

    python main.py player.png --grid 16x16 --sprite 0,0,1,1 --out player.h\n
    "},{"location":"tools/sprite_compiler/usage_guide/#multiple-sprites","title":"Multiple Sprites","text":"

    Convert multiple sprites from a single sheet:

    python main.py sheet.png --grid 16x16 \\\n  --sprite 0,0,1,1 \\\n  --sprite 1,0,1,1 \\\n  --sprite 2,0,1,1 \\\n  --out animations.h\n
    "},{"location":"tools/sprite_compiler/usage_guide/#export-modes","title":"Export Modes","text":"

    Layered (Default): Generates multiple uint16_t arrays, one for each color layer. Best for standard PixelRoot32 rendering.

    python main.py icon.png --grid 16x16 --sprite 0,0,1,1 --mode layered\n

    Packed 2bpp: Generates a single array with 2 bits per pixel (4 colors max).

    python main.py icon.png --grid 16x16 --sprite 0,0,1,1 --mode 2bpp\n
    "},{"location":"tools/sprite_compiler/usage_guide/#step-by-step-examples","title":"Step-by-Step Examples","text":""},{"location":"tools/sprite_compiler/usage_guide/#example-1-simple-player-sprite","title":"Example 1: Simple Player Sprite","text":"

    Step 1: Create Image

    • Create an 8x8 pixel PNG image
    • Use black and white colors
    • Save as player.png

    Step 2: Compile

    python main.py player.png --grid 8x8 --sprite 0,0,1,1 --prefix PLAYER --out player_sprite.h\n

    Step 3: Use in Code

    #include \"player_sprite.h\"\n\nvoid draw() {\n    // PLAYER_SPRITE_0_LAYER_0 is generated automatically\n    renderer.drawSprite(PLAYER_SPRITE_0_LAYER_0, 100, 100, Color::White);\n}\n
    "},{"location":"tools/sprite_compiler/usage_guide/#example-2-multiple-animation-frames","title":"Example 2: Multiple Animation Frames","text":"

    Step 1: Prepare Images

    • Create frames: walk_0.png, walk_1.png, walk_2.png
    • All same size (e.g., 16x16)

    Step 2: Batch Compile

    pr32-sprite-compiler --batch walk_*.png --output-dir animations/ --prefix WALK_\n

    Step 3: Use in Animation

    #include \"animations/walk_0.h\"\n#include \"animations/walk_1.h\"\n#include \"animations/walk_2.h\"\n\nconst Sprite* WALK_FRAMES[] = {\n    &WALK_0_SPRITE,\n    &WALK_1_SPRITE,\n    &WALK_2_SPRITE\n};\n
    "},{"location":"tools/sprite_compiler/usage_guide/#example-3-sprite-sheet","title":"Example 3: Sprite Sheet","text":"

    Step 1: Create Sprite Sheet

    • Create a 64x64 image with 4x4 grid of 16x16 sprites
    • Save as characters.png

    Step 2: Split Sheet

    pr32-sprite-compiler characters.png characters.h --sheet 16x16 --count 16\n

    Step 3: Use Individual Sprites

    #include \"characters.h\"\n\n// Sprites named CHARACTER_0, CHARACTER_1, etc.\nrenderer.drawSprite(CHARACTER_0, 50, 50, Color::White);\nrenderer.drawSprite(CHARACTER_1, 70, 50, Color::White);\n
    "},{"location":"tools/sprite_compiler/usage_guide/#batch-processing","title":"Batch Processing","text":""},{"location":"tools/sprite_compiler/usage_guide/#process-multiple-files","title":"Process Multiple Files","text":"

    Process all PNG files in a directory:

    pr32-sprite-compiler --batch sprites/*.png --output-dir generated/\n
    "},{"location":"tools/sprite_compiler/usage_guide/#with-options","title":"With Options","text":"

    Apply options to all files:

    pr32-sprite-compiler --batch assets/*.png \\\n    --output-dir src/sprites/ \\\n    --format 1bpp \\\n    --prefix SPRITE_\n
    "},{"location":"tools/sprite_compiler/usage_guide/#recursive-processing","title":"Recursive Processing","text":"

    Process subdirectories:

    pr32-sprite-compiler --batch assets/**/*.png --output-dir generated/\n
    "},{"location":"tools/sprite_compiler/usage_guide/#sprite-sheets","title":"Sprite Sheets","text":""},{"location":"tools/sprite_compiler/usage_guide/#automatic-splitting","title":"Automatic Splitting","text":"

    Split a sprite sheet into individual sprites:

    pr32-sprite-compiler sheet.png output.h --sheet 8x8 --count 16\n

    Parameters:

    • --sheet WxH: Tile size (width x height)
    • --count N: Number of sprites in sheet
    "},{"location":"tools/sprite_compiler/usage_guide/#grid-layout","title":"Grid Layout","text":"

    Specify grid dimensions:

    pr32-sprite-compiler sheet.png output.h \\\n    --sheet 16x16 \\\n    --grid 4x4 \\\n    --count 16\n

    Parameters:

    • --grid WxH: Grid dimensions (columns x rows)
    "},{"location":"tools/sprite_compiler/usage_guide/#custom-naming","title":"Custom Naming","text":"

    Name sprites with index:

    pr32-sprite-compiler sheet.png output.h \\\n    --sheet 8x8 \\\n    --count 8 \\\n    --prefix CHARACTER_ \\\n    --indexed\n

    Generates: CHARACTER_0, CHARACTER_1, etc.

    "},{"location":"tools/sprite_compiler/usage_guide/#custom-palettes","title":"Custom Palettes","text":""},{"location":"tools/sprite_compiler/usage_guide/#using-palette-file","title":"Using Palette File","text":"

    Convert with custom color palette:

    pr32-sprite-compiler sprite.png output.h --palette palette.json\n

    Palette JSON format:

    {\n  \"colors\": [\n    {\"r\": 0, \"g\": 0, \"b\": 0, \"name\": \"black\"},\n    {\"r\": 255, \"g\": 255, \"b\": 255, \"name\": \"white\"}\n  ]\n}\n
    "},{"location":"tools/sprite_compiler/usage_guide/#built-in-palettes","title":"Built-in Palettes","text":"

    Use predefined palettes:

    pr32-sprite-compiler sprite.png output.h --palette nes\npr32-sprite-compiler sprite.png output.h --palette gb\npr32-sprite-compiler sprite.png output.h --palette pico8\n
    "},{"location":"tools/sprite_compiler/usage_guide/#advanced-options","title":"Advanced Options","text":""},{"location":"tools/sprite_compiler/usage_guide/#threshold-for-grayscale","title":"Threshold for Grayscale","text":"

    Set threshold for black/white conversion:

    pr32-sprite-compiler sprite.png output.h --threshold 128\n

    Values: 0-255 (default: 127)

    "},{"location":"tools/sprite_compiler/usage_guide/#dithering","title":"Dithering","text":"

    Enable dithering for better gradients:

    pr32-sprite-compiler sprite.png output.h --dither\n
    "},{"location":"tools/sprite_compiler/usage_guide/#alignment","title":"Alignment","text":"

    Control output alignment:

    pr32-sprite-compiler sprite.png output.h --align 4\n
    "},{"location":"tools/sprite_compiler/usage_guide/#endianness","title":"Endianness","text":"

    Specify byte order:

    pr32-sprite-compiler sprite.png output.h --endian little\npr32-sprite-compiler sprite.png output.h --endian big\n
    "},{"location":"tools/sprite_compiler/usage_guide/#output-customization","title":"Output Customization","text":""},{"location":"tools/sprite_compiler/usage_guide/#namespace","title":"Namespace","text":"

    Wrap output in namespace:

    pr32-sprite-compiler sprite.png output.h --namespace MyGame\n
    "},{"location":"tools/sprite_compiler/usage_guide/#header-guard","title":"Header Guard","text":"

    Custom header guard:

    pr32-sprite-compiler sprite.png output.h --guard MY_SPRITE_H\n
    "},{"location":"tools/sprite_compiler/usage_guide/#include-paths","title":"Include Paths","text":"

    Custom include paths:

    pr32-sprite-compiler sprite.png output.h \\\n    --include \"<graphics/Sprite.h>\" \\\n    --include \"<stdint.h>\"\n
    "},{"location":"tools/sprite_compiler/usage_guide/#integration-with-build-systems","title":"Integration with Build Systems","text":""},{"location":"tools/sprite_compiler/usage_guide/#platformio","title":"PlatformIO","text":"

    Add to platformio.ini:

    [env:esp32dev]\nextra_scripts = \n    pre:scripts/compile_sprites.py\n

    compile_sprites.py:

    Import(\"env\")\nimport subprocess\n\nsubprocess.run([\n    \"pr32-sprite-compiler\",\n    \"--batch\", \"assets/sprites/*.png\",\n    \"--output-dir\", \"src/sprites/\"\n])\n
    "},{"location":"tools/sprite_compiler/usage_guide/#makefile","title":"Makefile","text":"
    SPRITES = $(wildcard assets/sprites/*.png)\nSPRITE_HEADERS = $(SPRITES:assets/sprites/%.png=src/sprites/%.h)\n\nsrc/sprites/%.h: assets/sprites/%.png\n pr32-sprite-compiler $< $@ --name $(shell basename $< .png | tr '[:lower:]' '[:upper:]')_SPRITE\n\nsprites: $(SPRITE_HEADERS)\n
    "},{"location":"tools/sprite_compiler/usage_guide/#cmake","title":"CMake","text":"
    file(GLOB SPRITE_FILES \"assets/sprites/*.png\")\n\nforeach(SPRITE ${SPRITE_FILES})\n    get_filename_component(SPRITE_NAME ${SPRITE} NAME_WE)\n    add_custom_command(\n        OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/sprites/${SPRITE_NAME}.h\n        COMMAND pr32-sprite-compiler\n        ARGS ${SPRITE} ${CMAKE_CURRENT_SOURCE_DIR}/src/sprites/${SPRITE_NAME}.h\n        DEPENDS ${SPRITE}\n    )\nendforeach()\n
    "},{"location":"tools/sprite_compiler/usage_guide/#gui-usage-if-available","title":"GUI Usage (If Available)","text":""},{"location":"tools/sprite_compiler/usage_guide/#opening-gui","title":"Opening GUI","text":"
    pr32-sprite-compiler --gui\n

    Or launch the GUI application directly.

    "},{"location":"tools/sprite_compiler/usage_guide/#gui-workflow","title":"GUI Workflow","text":"
    1. Drag and drop images into the window
    2. Preview sprite data in real-time
    3. Adjust settings visually (format, threshold, etc.)
    4. Export to header files
    5. Batch process multiple files
    "},{"location":"tools/sprite_compiler/usage_guide/#gui-features","title":"GUI Features","text":"
    • Visual preview of sprite conversion
    • Real-time threshold adjustment
    • Palette selection
    • Batch processing interface
    • Export options
    "},{"location":"tools/sprite_compiler/usage_guide/#best-practices","title":"Best Practices","text":""},{"location":"tools/sprite_compiler/usage_guide/#image-preparation","title":"Image Preparation","text":"
    • Use indexed color PNG for best results
    • Keep sprites small (8x8, 16x16, 32x32)
    • Use black and white for 1bpp
    • Limit colors for 2bpp/4bpp formats
    "},{"location":"tools/sprite_compiler/usage_guide/#file-organization","title":"File Organization","text":"
    project/\n\u251c\u2500\u2500 assets/\n\u2502   \u2514\u2500\u2500 sprites/\n\u2502       \u251c\u2500\u2500 player.png\n\u2502       \u251c\u2500\u2500 enemy.png\n\u2502       \u2514\u2500\u2500 items.png\n\u251c\u2500\u2500 src/\n\u2502   \u2514\u2500\u2500 sprites/          # Generated headers\n\u2502       \u251c\u2500\u2500 player.h\n\u2502       \u251c\u2500\u2500 enemy.h\n\u2502       \u2514\u2500\u2500 items.h\n\u2514\u2500\u2500 platformio.ini\n
    "},{"location":"tools/sprite_compiler/usage_guide/#naming-conventions","title":"Naming Conventions","text":"
    • Use descriptive names: player_walk_0.png \u2192 PLAYER_WALK_0_SPRITE
    • Be consistent: All caps for sprite names
    • Use prefixes: ENEMY_, PLAYER_, ITEM_
    "},{"location":"tools/sprite_compiler/usage_guide/#version-control","title":"Version Control","text":"
    • Commit generated headers (they're part of the build)
    • Or add to .gitignore and regenerate on build
    • Keep source images in version control
    "},{"location":"tools/sprite_compiler/usage_guide/#troubleshooting","title":"Troubleshooting","text":""},{"location":"tools/sprite_compiler/usage_guide/#common-issues","title":"Common Issues","text":"

    \"Image too large\":

    • Sprites must be \u2264 16 pixels wide for 1bpp
    • Resize image or split into multiple sprites

    \"Colors not converting correctly\":

    • Use indexed color PNG
    • For 1bpp: Use only black and white
    • For 2bpp: Use exactly 4 colors
    • For 4bpp: Use up to 16 colors

    \"Output file not found\":

    • Check write permissions
    • Verify output directory exists
    • Use absolute paths if needed

    \"Invalid format\":

    • Ensure input is PNG format
    • Check file is not corrupted
    • Try re-saving image in image editor
    "},{"location":"tools/sprite_compiler/usage_guide/#getting-help","title":"Getting Help","text":"
    pr32-sprite-compiler --help\n

    Shows all available options and usage.

    "},{"location":"tools/sprite_compiler/usage_guide/#next-steps","title":"Next Steps","text":"
    • Advanced Features - Explore advanced options
    • Overview - Learn more about the compiler
    • Manual - Sprites - Using sprites in games
    "},{"location":"tools/sprite_compiler/usage_guide/#see-also","title":"See Also","text":"
    • Code Examples - Sprites - Sprite usage examples
    • Troubleshooting - Common issues and solutions
    "},{"location":"tools/tilemap_editor/installation/","title":"Installation Guide","text":"

    The PixelRoot32 Tilemap Editor can be run directly from source or as a standalone executable on Windows.

    "},{"location":"tools/tilemap_editor/installation/#1-requirements","title":"1. Requirements","text":"
    • Python 3.13+ (if running from source).
    • Windows 10/11 (recommended).
    "},{"location":"tools/tilemap_editor/installation/#2-install-from-source","title":"2. Install from Source","text":""},{"location":"tools/tilemap_editor/installation/#21-clone-the-repository","title":"2.1 Clone the Repository","text":"
    git clone https://github.com/Gperez88/PixelRoot32-Tilemap-Editor.git\ncd PixelRoot32-Tilemap-Editor\n
    "},{"location":"tools/tilemap_editor/installation/#22-install-dependencies","title":"2.2 Install Dependencies","text":"

    The editor uses several Python libraries for the GUI and image processing:

    pip install ttkbootstrap pillow jinja2\n
    "},{"location":"tools/tilemap_editor/installation/#23-run-the-editor","title":"2.3 Run the Editor","text":"
    python main.py\n
    "},{"location":"tools/tilemap_editor/installation/#3-standalone-executable-windows","title":"3. Standalone Executable (Windows)","text":"

    For a more convenient experience, you can use the pre-compiled version:

    1. Go to the Releases section of the repository.
    2. Download the latest PixelRoot32-Editor-win64.zip.
    3. Extract the contents to a folder.
    4. Run PixelRoot32-Editor.exe.

    Note: No Python installation is required to run the standalone executable.

    "},{"location":"tools/tilemap_editor/installation/#4-building-your-own-executable","title":"4. Building your own Executable","text":"

    If you want to package the editor yourself:

    1. Install PyInstaller:
    pip install pyinstaller\n
    1. Run the build command using the provided .spec file:
    pyinstaller pixelroot32_editor.spec\n
    1. The executable will be available in the dist/ folder.
    "},{"location":"tools/tilemap_editor/overview/","title":"Tilemap Editor Overview","text":"

    The PixelRoot32 Tilemap Editor is a powerful visual tool designed to create complex multi-layered tile-based maps for the PixelRoot32 engine. It simplifies the process of designing game environments, managing tilesets, and exporting optimized C++ code.

    "},{"location":"tools/tilemap_editor/overview/#what-it-does","title":"What It Does","text":"

    The Tilemap Editor allows you to:

    • Visual Design: Paint tiles directly onto a canvas with layers and transparency.
    • Tileset Management: Import PNG images as tilesets and select single or multiple tiles.
    • Multi-Layer Support: Organize your map into up to 8 layers for parallax effects or depth.
    • Optimized Export: Generate C++ header and source files compatible with the PixelRoot32 renderer.
    • BPP Support: Export maps in 1bpp, 2bpp, or 4bpp formats to balance memory usage and color depth.
    "},{"location":"tools/tilemap_editor/overview/#key-features","title":"Key Features","text":""},{"location":"tools/tilemap_editor/overview/#visual-editing-tools","title":"\u2705 Visual Editing Tools","text":"
    • Brush: Paint individual tiles or patterns.
    • Eraser: Remove tiles from the active layer.
    • Rectangle Fill: Quickly fill areas with a specific tile.
    • Pipette: Pick an existing tile from the canvas.
    "},{"location":"tools/tilemap_editor/overview/#multi-layer-system","title":"\u2705 Multi-Layer System","text":"
    • Visibility Toggle: Hide/show layers to focus on specific parts of the map.
    • Opacity Control: Adjust layer transparency for complex blending effects.
    • Layer Reordering: Change the render order of your tilemaps.
    "},{"location":"tools/tilemap_editor/overview/#tileset-selector","title":"\u2705 Tileset Selector","text":"
    • Smart Selection: Drag and select a rectangular area of tiles.
    • Multiple Tilesets: Support for multiple tilesets per project (planned).
    • Auto-import: Automatically detects tile size from the imported image.
    "},{"location":"tools/tilemap_editor/overview/#engine-integration","title":"\u2705 Engine Integration","text":"
    • Workspace Selection: Link the editor to your PixelRoot32 projects directory.
    • Direct Export: Files are generated with the correct namespaces and structures for immediate use.
    • BPP Compatibility: Ensures exported data matches the engine's expected format for 1bpp, 2bpp, and 4bpp.
    "},{"location":"tools/tilemap_editor/overview/#data-formats","title":"Data Formats","text":""},{"location":"tools/tilemap_editor/overview/#project-file-pr32scene","title":"Project File (.pr32scene)","text":"

    The editor uses a custom JSON-based format to save your project state, including:

    • Tileset metadata (path, tile size, spacing).
    • Layer data (tile indices, width, height, position).
    • Project settings (BPP, namespace).
    "},{"location":"tools/tilemap_editor/overview/#exported-c","title":"Exported C++","text":"

    The editor generates <namespace>.h and <namespace>.cpp files containing:

    • Tilemap Data: One packed array of tile indices per layer (*_INDICES[]). Each layer is exposed as a TileMap4bpp (or TileMap2bpp/TileMap) with an indices pointer; use the same data for rendering and for tile-based collision in your game code.
    • Tilemap Structures: pixelroot32::graphics::TileMap (or TileMap2bpp/TileMap4bpp) definitions, plus tileset pool and palette.
    • Export options: Store data in Flash (ESP32) (default) emits static data with PROGMEM to reduce RAM use; Legacy format disables Flash attributes for backward compatibility or non-ESP32 builds.
    • Scene init: Call init() once before drawing; the generated code registers the palette and configures each layer for the engine.
    "},{"location":"tools/tilemap_editor/usage_guide/","title":"Usage Guide","text":"

    This guide covers the basic workflow for creating and exporting a tilemap using the PixelRoot32 Tilemap Editor.

    "},{"location":"tools/tilemap_editor/usage_guide/#1-creating-a-new-project","title":"1. Creating a New Project","text":"
    1. Launch the editor.
    2. Go to File > New Project.
    3. Enter the project name and select the base Color Depth (BPP):
    4. 1bpp: Monochrome (2 colors).
    5. 2bpp: 4 colors.
    6. 4bpp: 16 colors.
    7. Set the Tile Size (e.g., 8x8, 16x16).
    "},{"location":"tools/tilemap_editor/usage_guide/#2-importing-a-tileset","title":"2. Importing a Tileset","text":"
    1. In the Tileset panel, click on Load Tileset.
    2. Select a PNG image containing your tiles.
    3. The image will be sliced into tiles based on the tile size set in the project.
    "},{"location":"tools/tilemap_editor/usage_guide/#3-painting-tiles","title":"3. Painting Tiles","text":"
    1. Select a tile (or a range of tiles) from the Tileset panel.
    2. Select the Brush tool (Shortcut: B).
    3. Click and drag on the canvas to paint.
    4. Use the Layers panel to switch between different layers.
    "},{"location":"tools/tilemap_editor/usage_guide/#4-selection-and-transformations","title":"4. Selection and Transformations","text":"
    • Single Selection: Click on a tile in the tileset.
    • Area Selection: Click and drag in the tileset to select a rectangular block of tiles.
    • Pipette: Press P and click on the canvas to pick the tile under the cursor.
    "},{"location":"tools/tilemap_editor/usage_guide/#5-exporting-to-c","title":"5. Exporting to C++","text":"
    1. Ensure you have at least one tileset imported.
    2. Click the Export button in the top right (or File > Export / Ctrl+E).
    3. In the export dialog:
    4. Set the C++ Namespace (e.g. forest_level); it defaults to the project name.
    5. Review the Color Depth (BPP); it is auto-detected from your tileset colors (1bpp, 2bpp, or 4bpp).
    6. Store data in Flash (ESP32): Checked by default; emits PROGMEM for palette, tileset, and layer data to reduce RAM on ESP32.
    7. Legacy format (no Flash attribute): Use for older projects or non-ESP32 builds.
    8. Click Export Now and choose the output directory.
    9. The editor generates:
    10. <namespace>.h: Declarations (e.g. TILE_SIZE, MAP_WIDTH, MAP_HEIGHT, layer TileMap4bpp externs, init()).
    11. <namespace>.cpp: Definitions (palette, tileset pool, layer indices, init() implementation). Use each layer's .indices in your game for drawing and tile-based collision.
    "},{"location":"tools/tilemap_editor/usage_guide/#6-keyboard-shortcuts","title":"6. Keyboard Shortcuts","text":"Shortcut Action B Brush Tool E Eraser Tool R Rectangle Fill Tool P Pipette Tool Space + Drag Pan Canvas Mouse Wheel Zoom In/Out Ctrl + N New Project Ctrl + S Save Project Ctrl + E Export Project Esc Close floating panels"}]} \ No newline at end of file diff --git a/site/tools/sprite_compiler/advanced_features/index.html b/site/tools/sprite_compiler/advanced_features/index.html index d3d8b57..900fa12 100644 --- a/site/tools/sprite_compiler/advanced_features/index.html +++ b/site/tools/sprite_compiler/advanced_features/index.html @@ -1,2 +1,2 @@ Advanced Features - PixelRoot32 Documentation

    Sprite Compiler Advanced Features

    Advanced features and options for the PixelRoot32 Sprite Compiler to optimize sprite conversion and handle complex scenarios.

    Automatic Palette Detection

    The Sprite Compiler automatically detects if your sprite uses one of the engine's built-in palettes. This simplifies your workflow and ensures color consistency.

    Predefined Engine Palettes

    The engine includes 5 optimized palettes: - PR32 (Default PixelRoot32 palette) - NES (Nintendo style) - GB (GameBoy style) - GBC (GameBoy Color style) - PICO8 (Fantasy console style)

    How it works

    1. Detection: When you compile an image, the tool compares all unique colors found in the sprite with the colors in the 5 engine palettes.
    2. Match: If all colors in your sprite belong to one of these palettes, the compiler:
    3. Omits generating a color array in the header.
    4. Assumes you will use the engine's built-in palette definitions at runtime.
    5. Custom Palette: If your sprite uses colors not found in the engine palettes, it automatically generates a {PREFIX}_PALETTE_MAPPING[16] array in the header file.

    Naming with Prefixes

    You can organize your generated code by using the --prefix parameter (or the Prefix field in the GUI).

    Using Prefixes

    By default, sprites are named SPRITE_N_.... Using a prefix allows you to create more descriptive names and avoid naming collisions.

    python main.py sheet.png --grid 16x16 --sprite 0,0,1,1 --prefix PLAYER_JUM
    -

    Generated names will follow this pattern: - PLAYER_JUM_SPRITE_0_LAYER_0 - PLAYER_JUM_SPRITE_0_2BPP - PLAYER_JUM_SPRITE_0_4BPP - PLAYER_JUM_PALETTE_MAPPING (if a custom palette is used)

    Export Modes

    Layered (1bpp)

    Best for standard PixelRoot32 rendering. It extracts each color into its own bitmask (1bpp). The engine then renders these layers sequentially.

    Packed (2bpp / 4bpp)

    Generates a single packed array where each pixel uses multiple bits. - 2bpp: 4 colors max (Index 0 is always transparent). - 4bpp: 16 colors max (Index 0 is always transparent).

    These modes are more efficient for sprites with many colors as they require only a single draw call.

    \ No newline at end of file +

    Generated names will follow this pattern: - PLAYER_JUM_SPRITE_0_LAYER_0 - PLAYER_JUM_SPRITE_0_2BPP - PLAYER_JUM_SPRITE_0_4BPP - PLAYER_JUM_PALETTE_MAPPING (if a custom palette is used)

    Export Modes

    Layered (1bpp)

    Best for standard PixelRoot32 rendering. It extracts each color into its own bitmask (1bpp). The engine then renders these layers sequentially.

    Packed (2bpp / 4bpp)

    Generates a single packed array where each pixel uses multiple bits. - 2bpp: 4 colors max (Index 0 is always transparent). - 4bpp: 16 colors max (Index 0 is always transparent).

    These modes are more efficient for sprites with many colors as they require only a single draw call.

    \ No newline at end of file diff --git a/site/tools/sprite_compiler/installation/index.html b/site/tools/sprite_compiler/installation/index.html index 982f241..5d3e2a4 100644 --- a/site/tools/sprite_compiler/installation/index.html +++ b/site/tools/sprite_compiler/installation/index.html @@ -29,4 +29,4 @@ npm install

    Uninstallation

    Remove Global Installation

    npm uninstall -g pr32-sprite-compiler
     

    Remove Local Installation

    npm uninstall pr32-sprite-compiler
    -

    Troubleshooting

    Common Issues

    "Command not found" after installation: - Restart your terminal - Check npm global bin path: npm config get prefix - Verify PATH includes npm bin directory

    Permission errors: - On Linux/macOS: Use sudo for global install - Or install locally without -g flag - On Windows: Run terminal as Administrator

    Module not found errors: - Reinstall: npm install -g pr32-sprite-compiler - Clear npm cache: npm cache clean --force

    Version conflicts: - Check Node.js version: node --version - Update Node.js if version is too old - Use nvm (Node Version Manager) to manage versions

    Getting Help

    Next Steps

    Once installed, proceed to: - Usage Guide - Learn how to use the compiler - Advanced Features - Explore advanced options

    See Also

    \ No newline at end of file +

    Troubleshooting

    Common Issues

    "Command not found" after installation: - Restart your terminal - Check npm global bin path: npm config get prefix - Verify PATH includes npm bin directory

    Permission errors: - On Linux/macOS: Use sudo for global install - Or install locally without -g flag - On Windows: Run terminal as Administrator

    Module not found errors: - Reinstall: npm install -g pr32-sprite-compiler - Clear npm cache: npm cache clean --force

    Version conflicts: - Check Node.js version: node --version - Update Node.js if version is too old - Use nvm (Node Version Manager) to manage versions

    Getting Help

    Next Steps

    Once installed, proceed to: - Usage Guide - Learn how to use the compiler - Advanced Features - Explore advanced options

    See Also

    \ No newline at end of file diff --git a/site/tools/sprite_compiler/overview/index.html b/site/tools/sprite_compiler/overview/index.html index 357577c..5cb599f 100644 --- a/site/tools/sprite_compiler/overview/index.html +++ b/site/tools/sprite_compiler/overview/index.html @@ -24,4 +24,4 @@ # Continue with your build process platformio run -

    Advantages Over Manual Creation

    ✅ Time Saving

    • No manual bit pattern conversion
    • Automatic format optimization
    • Batch processing multiple sprites

    ✅ Accuracy

    • Correct bit ordering
    • Proper array formatting
    • Valid C++ syntax

    ✅ Consistency

    • Uniform naming conventions
    • Standardized output format
    • Consistent code structure

    ✅ Maintainability

    • Easy to regenerate from source images
    • Version control friendly
    • Clear separation of assets and code

    Limitations

    • Width limit: 16 pixels for 1bpp (hardware constraint)
    • Color depth: Limited by format (1bpp = 2 colors, 2bpp = 4 colors, etc.)
    • File format: Primarily PNG (other formats may require conversion)

    Next Steps

    See Also

    \ No newline at end of file +

    Advantages Over Manual Creation

    ✅ Time Saving

    • No manual bit pattern conversion
    • Automatic format optimization
    • Batch processing multiple sprites

    ✅ Accuracy

    • Correct bit ordering
    • Proper array formatting
    • Valid C++ syntax

    ✅ Consistency

    • Uniform naming conventions
    • Standardized output format
    • Consistent code structure

    ✅ Maintainability

    • Easy to regenerate from source images
    • Version control friendly
    • Clear separation of assets and code

    Limitations

    • Width limit: 16 pixels for 1bpp (hardware constraint)
    • Color depth: Limited by format (1bpp = 2 colors, 2bpp = 4 colors, etc.)
    • File format: Primarily PNG (other formats may require conversion)

    Next Steps

    See Also

    \ No newline at end of file diff --git a/site/tools/sprite_compiler/usage_guide/index.html b/site/tools/sprite_compiler/usage_guide/index.html index 0ddae19..b97fa27 100644 --- a/site/tools/sprite_compiler/usage_guide/index.html +++ b/site/tools/sprite_compiler/usage_guide/index.html @@ -110,4 +110,4 @@ │ └── items.h └── platformio.ini

    Naming Conventions

    • Use descriptive names: player_walk_0.pngPLAYER_WALK_0_SPRITE
    • Be consistent: All caps for sprite names
    • Use prefixes: ENEMY_, PLAYER_, ITEM_

    Version Control

    • Commit generated headers (they're part of the build)
    • Or add to .gitignore and regenerate on build
    • Keep source images in version control

    Troubleshooting

    Common Issues

    "Image too large":

    • Sprites must be ≤ 16 pixels wide for 1bpp
    • Resize image or split into multiple sprites

    "Colors not converting correctly":

    • Use indexed color PNG
    • For 1bpp: Use only black and white
    • For 2bpp: Use exactly 4 colors
    • For 4bpp: Use up to 16 colors

    "Output file not found":

    • Check write permissions
    • Verify output directory exists
    • Use absolute paths if needed

    "Invalid format":

    • Ensure input is PNG format
    • Check file is not corrupted
    • Try re-saving image in image editor

    Getting Help

    pr32-sprite-compiler --help
    -

    Shows all available options and usage.

    Next Steps

    See Also

    \ No newline at end of file +

    Shows all available options and usage.

    Next Steps

    See Also

    \ No newline at end of file diff --git a/site/tools/tilemap_editor/installation/index.html b/site/tools/tilemap_editor/installation/index.html index 9a166ba..c9c64cb 100644 --- a/site/tools/tilemap_editor/installation/index.html +++ b/site/tools/tilemap_editor/installation/index.html @@ -4,4 +4,4 @@

    2.3 Run the Editor

    python main.py
     

    3. Standalone Executable (Windows)

    For a more convenient experience, you can use the pre-compiled version:

    1. Go to the Releases section of the repository.
    2. Download the latest PixelRoot32-Editor-win64.zip.
    3. Extract the contents to a folder.
    4. Run PixelRoot32-Editor.exe.

    Note: No Python installation is required to run the standalone executable.

    4. Building your own Executable

    If you want to package the editor yourself:

    1. Install PyInstaller:
    pip install pyinstaller
     
    1. Run the build command using the provided .spec file:
    pyinstaller pixelroot32_editor.spec
    -
    1. The executable will be available in the dist/ folder.
    \ No newline at end of file +
    1. The executable will be available in the dist/ folder.
    \ No newline at end of file diff --git a/site/tools/tilemap_editor/overview/index.html b/site/tools/tilemap_editor/overview/index.html index c5f0c2a..3edff91 100644 --- a/site/tools/tilemap_editor/overview/index.html +++ b/site/tools/tilemap_editor/overview/index.html @@ -1 +1 @@ - Tilemap Editor Overview - PixelRoot32 Documentation

    Tilemap Editor Overview

    The PixelRoot32 Tilemap Editor is a powerful visual tool designed to create complex multi-layered tile-based maps for the PixelRoot32 engine. It simplifies the process of designing game environments, managing tilesets, and exporting optimized C++ code.

    What It Does

    The Tilemap Editor allows you to:

    • Visual Design: Paint tiles directly onto a canvas with layers and transparency.
    • Tileset Management: Import PNG images as tilesets and select single or multiple tiles.
    • Multi-Layer Support: Organize your map into up to 8 layers for parallax effects or depth.
    • Optimized Export: Generate C++ header and source files compatible with the PixelRoot32 renderer.
    • BPP Support: Export maps in 1bpp, 2bpp, or 4bpp formats to balance memory usage and color depth.

    Key Features

    ✅ Visual Editing Tools

    • Brush: Paint individual tiles or patterns.
    • Eraser: Remove tiles from the active layer.
    • Rectangle Fill: Quickly fill areas with a specific tile.
    • Pipette: Pick an existing tile from the canvas.

    ✅ Multi-Layer System

    • Visibility Toggle: Hide/show layers to focus on specific parts of the map.
    • Opacity Control: Adjust layer transparency for complex blending effects.
    • Layer Reordering: Change the render order of your tilemaps.

    ✅ Tileset Selector

    • Smart Selection: Drag and select a rectangular area of tiles.
    • Multiple Tilesets: Support for multiple tilesets per project (planned).
    • Auto-import: Automatically detects tile size from the imported image.

    ✅ Engine Integration

    • Workspace Selection: Link the editor to your PixelRoot32 projects directory.
    • Direct Export: Files are generated with the correct namespaces and structures for immediate use.
    • BPP Compatibility: Ensures exported data matches the engine's expected format for 1bpp, 2bpp, and 4bpp.

    Data Formats

    Project File (.pr32scene)

    The editor uses a custom JSON-based format to save your project state, including:

    • Tileset metadata (path, tile size, spacing).
    • Layer data (tile indices, width, height, position).
    • Project settings (BPP, namespace).

    Exported C++

    The editor generates .h and .cpp files containing:

    • Tilemap Data: Packed arrays of tile indices.
    • Tilemap Structures: pixelroot32::graphics::TileMap (or TileMap2bpp/TileMap4bpp) definitions.
    • Scene Headers: Convenient entry points for loading entire maps into your game.
    \ No newline at end of file + Tilemap Editor Overview - PixelRoot32 Documentation

    Tilemap Editor Overview

    The PixelRoot32 Tilemap Editor is a powerful visual tool designed to create complex multi-layered tile-based maps for the PixelRoot32 engine. It simplifies the process of designing game environments, managing tilesets, and exporting optimized C++ code.

    What It Does

    The Tilemap Editor allows you to:

    • Visual Design: Paint tiles directly onto a canvas with layers and transparency.
    • Tileset Management: Import PNG images as tilesets and select single or multiple tiles.
    • Multi-Layer Support: Organize your map into up to 8 layers for parallax effects or depth.
    • Optimized Export: Generate C++ header and source files compatible with the PixelRoot32 renderer.
    • BPP Support: Export maps in 1bpp, 2bpp, or 4bpp formats to balance memory usage and color depth.

    Key Features

    ✅ Visual Editing Tools

    • Brush: Paint individual tiles or patterns.
    • Eraser: Remove tiles from the active layer.
    • Rectangle Fill: Quickly fill areas with a specific tile.
    • Pipette: Pick an existing tile from the canvas.

    ✅ Multi-Layer System

    • Visibility Toggle: Hide/show layers to focus on specific parts of the map.
    • Opacity Control: Adjust layer transparency for complex blending effects.
    • Layer Reordering: Change the render order of your tilemaps.

    ✅ Tileset Selector

    • Smart Selection: Drag and select a rectangular area of tiles.
    • Multiple Tilesets: Support for multiple tilesets per project (planned).
    • Auto-import: Automatically detects tile size from the imported image.

    ✅ Engine Integration

    • Workspace Selection: Link the editor to your PixelRoot32 projects directory.
    • Direct Export: Files are generated with the correct namespaces and structures for immediate use.
    • BPP Compatibility: Ensures exported data matches the engine's expected format for 1bpp, 2bpp, and 4bpp.

    Data Formats

    Project File (.pr32scene)

    The editor uses a custom JSON-based format to save your project state, including:

    • Tileset metadata (path, tile size, spacing).
    • Layer data (tile indices, width, height, position).
    • Project settings (BPP, namespace).

    Exported C++

    The editor generates <namespace>.h and <namespace>.cpp files containing:

    • Tilemap Data: One packed array of tile indices per layer (*_INDICES[]). Each layer is exposed as a TileMap4bpp (or TileMap2bpp/TileMap) with an indices pointer; use the same data for rendering and for tile-based collision in your game code.
    • Tilemap Structures: pixelroot32::graphics::TileMap (or TileMap2bpp/TileMap4bpp) definitions, plus tileset pool and palette.
    • Export options: Store data in Flash (ESP32) (default) emits static data with PROGMEM to reduce RAM use; Legacy format disables Flash attributes for backward compatibility or non-ESP32 builds.
    • Scene init: Call init() once before drawing; the generated code registers the palette and configures each layer for the engine.
    \ No newline at end of file diff --git a/site/tools/tilemap_editor/usage_guide/index.html b/site/tools/tilemap_editor/usage_guide/index.html index d363dd8..04eb3a8 100644 --- a/site/tools/tilemap_editor/usage_guide/index.html +++ b/site/tools/tilemap_editor/usage_guide/index.html @@ -1 +1 @@ - Usage Guide - PixelRoot32 Documentation

    Usage Guide

    This guide covers the basic workflow for creating and exporting a tilemap using the PixelRoot32 Tilemap Editor.

    1. Creating a New Project

    1. Launch the editor.
    2. Go to File > New Project.
    3. Enter the project name and select the base Color Depth (BPP):
    4. 1bpp: Monochrome (2 colors).
    5. 2bpp: 4 colors.
    6. 4bpp: 16 colors.
    7. Set the Tile Size (e.g., 8x8, 16x16).

    2. Importing a Tileset

    1. In the Tileset panel, click on Load Tileset.
    2. Select a PNG image containing your tiles.
    3. The image will be sliced into tiles based on the tile size set in the project.

    3. Painting Tiles

    1. Select a tile (or a range of tiles) from the Tileset panel.
    2. Select the Brush tool (Shortcut: B).
    3. Click and drag on the canvas to paint.
    4. Use the Layers panel to switch between different layers.

    4. Selection and Transformations

    • Single Selection: Click on a tile in the tileset.
    • Area Selection: Click and drag in the tileset to select a rectangular block of tiles.
    • Pipette: Press P and click on the canvas to pick the tile under the cursor.

    5. Exporting to C++

    1. Ensure your Workspace Path is correctly set in Project Settings. This should point to your PixelRoot32 project's src or include directory.
    2. Click on File > Export.
    3. The editor will generate:
    4. <ProjectName>_data.h and .cpp: Containing the raw tile data and structures.
    5. <ProjectName>_scene.h: A high-level header to include in your game.

    6. Keyboard Shortcuts

    Shortcut Action
    B Brush Tool
    E Eraser Tool
    R Rectangle Fill Tool
    P Pipette Tool
    Space + Drag Pan Canvas
    Mouse Wheel Zoom In/Out
    Ctrl + N New Project
    Ctrl + S Save Project
    Ctrl + E Export Project
    Esc Close floating panels
    \ No newline at end of file + Usage Guide - PixelRoot32 Documentation

    Usage Guide

    This guide covers the basic workflow for creating and exporting a tilemap using the PixelRoot32 Tilemap Editor.

    1. Creating a New Project

    1. Launch the editor.
    2. Go to File > New Project.
    3. Enter the project name and select the base Color Depth (BPP):
    4. 1bpp: Monochrome (2 colors).
    5. 2bpp: 4 colors.
    6. 4bpp: 16 colors.
    7. Set the Tile Size (e.g., 8x8, 16x16).

    2. Importing a Tileset

    1. In the Tileset panel, click on Load Tileset.
    2. Select a PNG image containing your tiles.
    3. The image will be sliced into tiles based on the tile size set in the project.

    3. Painting Tiles

    1. Select a tile (or a range of tiles) from the Tileset panel.
    2. Select the Brush tool (Shortcut: B).
    3. Click and drag on the canvas to paint.
    4. Use the Layers panel to switch between different layers.

    4. Selection and Transformations

    • Single Selection: Click on a tile in the tileset.
    • Area Selection: Click and drag in the tileset to select a rectangular block of tiles.
    • Pipette: Press P and click on the canvas to pick the tile under the cursor.

    5. Exporting to C++

    1. Ensure you have at least one tileset imported.
    2. Click the Export button in the top right (or File > Export / Ctrl+E).
    3. In the export dialog:
    4. Set the C++ Namespace (e.g. forest_level); it defaults to the project name.
    5. Review the Color Depth (BPP); it is auto-detected from your tileset colors (1bpp, 2bpp, or 4bpp).
    6. Store data in Flash (ESP32): Checked by default; emits PROGMEM for palette, tileset, and layer data to reduce RAM on ESP32.
    7. Legacy format (no Flash attribute): Use for older projects or non-ESP32 builds.
    8. Click Export Now and choose the output directory.
    9. The editor generates:
    10. <namespace>.h: Declarations (e.g. TILE_SIZE, MAP_WIDTH, MAP_HEIGHT, layer TileMap4bpp externs, init()).
    11. <namespace>.cpp: Definitions (palette, tileset pool, layer indices, init() implementation). Use each layer's .indices in your game for drawing and tile-based collision.

    6. Keyboard Shortcuts

    Shortcut Action
    B Brush Tool
    E Eraser Tool
    R Rectangle Fill Tool
    P Pipette Tool
    Space + Drag Pan Canvas
    Mouse Wheel Zoom In/Out
    Ctrl + N New Project
    Ctrl + S Save Project
    Ctrl + E Export Project
    Esc Close floating panels
    \ No newline at end of file