diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..45ddf0a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +site/ diff --git a/docs/api_reference/core/engine.md b/docs/api_reference/core/engine.md index 9dd61b0..e21c728 100644 --- a/docs/api_reference/core/engine.md +++ b/docs/api_reference/core/engine.md @@ -30,54 +30,63 @@ namespace pixelroot32::core { Creates a new engine instance with custom display, input, and audio configurations. Uses move semantics for DisplayConfig to transfer ownership of resources (like custom draw surfaces). **Parameters:** + - `displayConfig` (`pixelroot32::graphics::DisplayConfig&&`): Configuration settings for the display (r-value reference) - `inputConfig` (const `pixelroot32::input::InputConfig&`): Configuration settings for the input system - `audioConfig` (const `pixelroot32::audio::AudioConfig&`): Configuration settings for the audio system -### Engine(const DisplayConfig& displayConfig, const InputConfig& inputConfig, const AudioConfig& audioConfig) +### Engine(DisplayConfig&& displayConfig, const InputConfig& inputConfig, const AudioConfig& audioConfig) -Creates a new engine instance by copying configurations. Note that copying DisplayConfig will not copy custom draw surfaces (they are unique_ptr). +Creates a new engine instance with custom display, input, and audio configurations. Uses move semantics for DisplayConfig to transfer ownership of resources (like custom draw surfaces). **Parameters:** -- `displayConfig` (const `pixelroot32::graphics::DisplayConfig&`): Configuration settings for the display + +- `displayConfig` (`pixelroot32::graphics::DisplayConfig&&`): Configuration settings for the display (r-value reference) - `inputConfig` (const `pixelroot32::input::InputConfig&`): Configuration settings for the input system - `audioConfig` (const `pixelroot32::audio::AudioConfig&`): Configuration settings for the audio system **Example:** + ```cpp #include "core/Engine.h" #include "graphics/DisplayConfig.h" // Example with move semantics (recommended for custom displays) -auto displayConfig = PIXELROOT32_CUSTOM_DISPLAY(std::make_unique().release(), 240, 240); +auto displayConfig = PIXELROOT32_CUSTOM_DISPLAY(new MyCustomDriver(), 240, 240); pixelroot32::core::Engine engine(std::move(displayConfig), inputConfig, audioConfig); ``` -### Engine(const DisplayConfig& displayConfig, const InputConfig& inputConfig) +### Engine(DisplayConfig&& displayConfig, const InputConfig& inputConfig) 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 + +- `displayConfig` (`pixelroot32::graphics::DisplayConfig&&`): Configuration settings for the display (r-value reference) - `inputConfig` (const `pixelroot32::input::InputConfig&`): Configuration settings for the input system **Example:** + ```cpp -pixelroot32::core::Engine engine(displayConfig, inputConfig); +auto displayConfig = /* ... */; +pixelroot32::core::Engine engine(std::move(displayConfig), inputConfig); engine.init(); engine.run(); ``` -### Engine(const DisplayConfig& displayConfig) +### Engine(DisplayConfig&& displayConfig) 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 + +- `displayConfig` (`pixelroot32::graphics::DisplayConfig&&`): Configuration settings for the display (r-value reference) **Example:** + ```cpp -pixelroot32::core::Engine engine(displayConfig); +auto displayConfig = /* ... */; +pixelroot32::core::Engine engine(std::move(displayConfig)); engine.init(); engine.run(); ``` @@ -89,14 +98,17 @@ engine.run(); 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:** + ```cpp Engine engine(displayConfig); engine.init(); // Initialize subsystems @@ -109,15 +121,18 @@ engine.run(); // Start game loop 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:** + ```cpp Engine engine(displayConfig); engine.init(); @@ -130,14 +145,17 @@ engine.run(); // Blocks here, runs game loop 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:** + ```cpp void update(unsigned long deltaTime) override { auto& engine = getEngine(); @@ -154,15 +172,18 @@ void update(unsigned long deltaTime) override { 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:** + ```cpp class MainMenuScene : public pixelroot32::core::Scene { // ... @@ -181,18 +202,21 @@ engine.setScene(&menuScene); // Start with menu engine.run(); ``` -### Scene* getCurrentScene() const +### std::optional getCurrentScene() const -Retrieves the currently active scene. +Retrieves the currently active scene, or std::nullopt if no scene is active. **Returns:** -- `Scene*`: Pointer to the current Scene, or `nullptr` if none is set + +- `std::optional`: Optional containing pointer to the current Scene, or std::nullopt if none is set **Example:** + ```cpp -auto* currentScene = engine.getCurrentScene(); -if (currentScene) { +auto currentScene = engine.getCurrentScene(); +if (currentScene.has_value()) { // Scene is active + Scene* scene = currentScene.value(); } ``` @@ -201,9 +225,11 @@ if (currentScene) { 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 @@ -213,9 +239,11 @@ Replaces the current renderer instance. Provides access to the Renderer subsystem. **Returns:** + - `pixelroot32::graphics::Renderer&`: Reference to the current Renderer **Example:** + ```cpp void draw(pixelroot32::graphics::Renderer& renderer) override { auto& engineRenderer = engine.getRenderer(); @@ -228,9 +256,11 @@ void draw(pixelroot32::graphics::Renderer& renderer) override { Provides access to the InputManager subsystem. **Returns:** + - `pixelroot32::input::InputManager&`: Reference to the InputManager **Example:** + ```cpp void update(unsigned long deltaTime) override { auto& input = engine.getInputManager(); @@ -245,9 +275,13 @@ void update(unsigned long deltaTime) override { Provides access to the AudioEngine subsystem. **Returns:** + - `pixelroot32::audio::AudioEngine&`: Reference to the AudioEngine +**Note:** Only available if `PIXELROOT32_ENABLE_AUDIO=1` + **Example:** + ```cpp void playSound() { auto& audio = engine.getAudioEngine(); @@ -264,13 +298,17 @@ void playSound() { Provides access to the MusicPlayer subsystem. **Returns:** + - `pixelroot32::audio::MusicPlayer&`: Reference to the MusicPlayer +**Note:** Only available if `PIXELROOT32_ENABLE_AUDIO=1` + **Example:** + ```cpp void playMusic() { auto& music = engine.getMusicPlayer(); - music.playTrack(myMusicTrack); + music.play(myMusicTrack); } ``` @@ -279,9 +317,11 @@ void playMusic() { Returns the detected hardware capabilities for the current platform (core count, recommended pinning, etc.). **Returns:** + - `const PlatformCapabilities&`: Reference to the detected capabilities. **Example:** + ```cpp const auto& caps = engine.getPlatformCapabilities(); if (caps.hasDualCore) { @@ -300,6 +340,7 @@ A structure that holds detected hardware capabilities, used to optimize task pin - **`int audioPriority`**: Recommended priority for audio tasks. Static Methods: + - **`static PlatformCapabilities detect()`**: Automatically detects hardware capabilities based on the platform and configuration. It respects the defaults defined in `platforms/PlatformDefaults.h` and any compile-time overrides. ## Optional: Debug Statistics Overlay @@ -313,8 +354,8 @@ When the engine is built with the preprocessor define **`PIXELROOT32_ENABLE_DEBU - **CPU**: Estimated processor load percentage (Yellow). !!! note "Platform Differences & CPU Metric" - The **CPU Load** metric is an estimation based on the processing time vs. a 60 FPS target (16.6ms). - + The **CPU Load** metric is an estimation based on the processing time vs. a 60 FPS target (16.6ms). + * **On ESP32**: This is a realistic representation of how much time the CPU is spending on engine logic within its power limits. * **On Native (PC)**: This metric may frequently show **100%** or high values even if your PC is idle. This happens because the native loop is synchronized with the monitor's VSYNC (via SDL2), which often causes the frame time to exceed 16.6ms (e.g., 33ms for 30 FPS). This is a result of the operating system's scheduling and SDL's synchronization, not actual hardware saturation. diff --git a/docs/api_reference/core/global_config.md b/docs/api_reference/core/global_config.md index 020f9c0..e09f649 100644 --- a/docs/api_reference/core/global_config.md +++ b/docs/api_reference/core/global_config.md @@ -2,6 +2,8 @@ The engine's behavior can be customized using `platforms/PlatformDefaults.h` and `platforms/EngineConfig.h`, or via compile-time build flags. This allows for fine-tuning performance and hardware support without modifying the core engine code. +For detailed platform-specific capabilities and limitations, see [Platform Compatibility Guide](../../manual/optimization/platform_compatibility.md). + ## Platform Macros (Build Flags) | Macro | Description | Default (ESP32) | @@ -13,6 +15,24 @@ The engine's behavior can be customized using `platforms/PlatformDefaults.h` and | `PIXELROOT32_USE_U8G2_DRIVER` | Enable U8G2 display driver support for monochromatic OLEDs. | Disabled | | `PIXELROOT32_NO_TFT_ESPI` | Disable default TFT_eSPI driver support. | Enabled | +### Modular Compilation Flags + +| Macro | Description | Default | +|-------|-------------|---------| +| `PIXELROOT32_ENABLE_AUDIO` | Enable audio subsystem (AudioEngine + MusicPlayer). | `1` | +| `PIXELROOT32_ENABLE_PHYSICS` | Enable physics system (CollisionSystem). | `1` | +| `PIXELROOT32_ENABLE_UI_SYSTEM` | Enable UI system (UIButton, UILabel, etc.). | `1` | +| `PIXELROOT32_ENABLE_PARTICLES` | Enable particle system. | `1` | + +**Usage in platformio.ini:** +```ini +[esp32_arcade] +extends = base_esp32, profile_arcade +build_flags = + ${base_esp32.build_flags} + ${profile_arcade.build_flags} +``` + ## Constants - **`DISPLAY_WIDTH`** @@ -38,3 +58,9 @@ The engine's behavior can be customized using `platforms/PlatformDefaults.h` and - **`SPATIAL_GRID_MAX_ENTITIES_PER_CELL`** Maximum entities stored in a single grid cell. Default is `24`. + +## See Also + +- [Platform Compatibility Guide](../../manual/optimization/platform_compatibility.md) +- [Performance Tuning](../../manual/optimization/performance_tuning.md) +- [Engine](engine.md) diff --git a/docs/api_reference/core/logging.md b/docs/api_reference/core/logging.md new file mode 100644 index 0000000..82cafe1 --- /dev/null +++ b/docs/api_reference/core/logging.md @@ -0,0 +1,429 @@ +# Logging API Reference + +The unified logging system provides cross-platform logging with automatic output routing to Serial (ESP32) or stdout (native platforms). It eliminates the need for platform-specific `#ifdef` blocks in logging code. + +--- + +## Including the Headers + +### General Game Code + +```cpp +#include "core/Log.h" +using namespace pixelroot32::core::logging; +``` + +### Platform-Specific Code + +```cpp +#include "platforms/PlatformLog.h" +using namespace pixelroot32::platforms::logging; +``` + +--- + +## Log Levels + +### LogLevel Enum + +```cpp +enum class LogLevel { + Info, // General information, debug messages + Warning, // Warnings, non-critical issues + Error // Errors, critical failures +}; +``` + +### Level Behavior + +| LogLevel | Output Prefix | Typical Use | +|----------|---------------|------------| +| `LogLevel::Info` | `[INFO]` | Debug information, state changes | +| `LogLevel::Warning` | `[WARN]` | Non-critical issues, performance warnings | +| `LogLevel::Error` | `[ERROR]` | Critical failures, system errors | + +--- + +## Core Functions + +### log(LogLevel, const char*, ...) - Explicit Level + +Log a message with a specified log level using printf-style formatting. + +```cpp +void log(LogLevel level, const char* format, ...); +``` + +**Parameters:** +- `level`: Log level (Info, Warning, Error) +- `format`: Printf-style format string +- `...`: Variable arguments + +**Example:** +```cpp +log(LogLevel::Info, "Player spawned at (%d, %d)", x, y); +log(LogLevel::Warning, "Memory usage: %d KB (threshold: %d KB)", used, threshold); +log(LogLevel::Error, "Failed to load sprite: %s", filename); +``` + +### log(const char*, ...) - Info Level Shorthand + +Log a message with Info level (shorthand convenience function). + +```cpp +void log(const char* format, ...); +``` + +**Parameters:** +- `format`: Printf-style format string +- `...`: Variable arguments + +**Example:** +```cpp +log("Game initialized successfully"); +log("FPS: %d, Entities: %d", fps, entityCount); +log("Level %d completed in %d seconds", level, time); +``` + +--- + +## Usage Examples + +### Basic Logging + +```cpp +#include "core/Log.h" +using namespace pixelroot32::core::logging; + +class Player { +public: + void update() { + log("Player position: (%d, %d)", x, y); + + if (health < 20) { + log(LogLevel::Warning, "Low health: %d/100", health); + } + + if (health <= 0) { + log(LogLevel::Error, "Player died!"); + } + } +}; +``` + +### Conditional Debug Logging + +```cpp +void updatePhysics() { +#ifdef PIXELROOT32_DEBUG_MODE + log("Physics update: %d bodies, %d contacts", bodyCount, contactCount); +#endif + + // Physics simulation... +} +``` + +### Error Handling + +```cpp +bool loadSprite(const char* filename) { + FILE* file = fopen(filename, "rb"); + if (!file) { + log(LogLevel::Error, "Cannot open sprite file: %s", filename); + return false; + } + + // Load sprite data... + log(LogLevel::Info, "Sprite loaded: %s (%d bytes)", filename, size); + return true; +} +``` + +### Performance Monitoring + +```cpp +class PerformanceMonitor { +private: + unsigned long lastTime; + int frameCount; + +public: + void update() { + frameCount++; + unsigned long currentTime = millis(); + + if (currentTime - lastTime >= 1000) { + log(LogLevel::Info, "FPS: %d", frameCount); + frameCount = 0; + lastTime = currentTime; + } + } +}; +``` + +--- + +## Platform-Specific Logging + +### Using PlatformLog.h + +For platform-specific code (drivers, low-level systems): + +```cpp +#include "platforms/PlatformLog.h" +using namespace pixelroot32::platforms::logging; + +void initDisplay() { + log(LogLevel::Info, "Initializing display driver"); + + if (!initializeHardware()) { + log(LogLevel::Error, "Display hardware initialization failed"); + return; + } + + log(LogLevel::Info, "Display initialized: %dx%d", width, height); +} +``` + +### Platform Differences + +| Platform | Output Destination | Formatting | +|----------|-------------------|------------| +| ESP32 | Serial (UART) | Arduino Serial.print/println | +| Native | stdout | printf | + +The API remains identical across platforms - only the output destination changes. + +--- + +## Advanced Usage + +### Logging Classes + +```cpp +class Logger { +private: + const char* component; + +public: + Logger(const char* name) : component(name) {} + + void info(const char* format, ...) const { + // Custom prefix with component name + char buffer[256]; + snprintf(buffer, sizeof(buffer), "[%s] %s", component, format); + + va_list args; + va_start(args, format); + // Implementation would call the core log function + va_end(args); + } + + void warning(const char* format, ...) const { + // Similar implementation for warnings + } + + void error(const char* format, ...) const { + // Similar implementation for errors + } +}; + +// Usage +Logger audioLogger("Audio"); +Logger physicsLogger("Physics"); + +audioLogger.info("Audio engine initialized"); +physicsLogger.warning("High collision count: %d", collisions); +``` + +### Structured Logging + +```cpp +struct LogEntry { + LogLevel level; + const char* system; + const char* message; + unsigned long timestamp; +}; + +class LogBuffer { +private: + LogEntry entries[32]; + int count; + +public: + void addEntry(LogLevel level, const char* system, const char* message) { + if (count < 32) { + entries[count] = {level, system, message, millis()}; + count++; + } + } + + void outputRecent() { + for (int i = 0; i < count; i++) { + const auto& entry = entries[i]; + log(entry.level, "[%s] %s", entry.system, entry.message); + } + count = 0; + } +}; +``` + +--- + +## Performance Considerations + +### ESP32 Platform + +- **Serial Output**: Uses Arduino Serial, which has some overhead +- **Buffering**: Consider buffering frequent debug messages +- **Baud Rate**: Ensure Serial baud rate is sufficient for output volume + +### Native Platform + +- **Standard I/O**: Uses printf, minimal overhead +- **Buffering**: stdout is typically line-buffered +- **Performance**: Suitable for frequent logging in debug builds + +### Optimization Tips + +```cpp +// Good: Conditional debug logging +#ifdef PIXELROOT32_DEBUG_MODE + log("Detailed debug info: %s", expensiveToString()); +#endif + +// Avoid: Expensive operations in release builds +log("Debug: %s", expensiveToString()); // Runs even in release +``` + +--- + +## Configuration + +### Debug Mode + +Control logging with compile-time flag: + +```cpp +// In platformio.ini or build configuration +build_flags = -D PIXELROOT32_DEBUG_MODE +``` + +```cpp +// In code +#ifdef PIXELROOT32_DEBUG_MODE + log("Debug information"); +#endif +``` + +### Log Level Filtering (Future Enhancement) + +While the current implementation outputs all log levels, you can implement filtering: + +```cpp +class FilteredLogger { +private: + LogLevel minLevel; + +public: + FilteredLogger(LogLevel level) : minLevel(level) {} + + void log(LogLevel level, const char* format, ...) { + if (level >= minLevel) { + // Call actual log function + } + } +}; +``` + +--- + +## Best Practices + +### 1. Use Appropriate Log Levels + +```cpp +// Info: General state changes +log("Player entered level %d", levelId); + +// Warning: Non-critical issues +log(LogLevel::Warning, "Frame time exceeded target: %d ms", frameTime); + +// Error: Critical failures +log(LogLevel::Error, "Failed to save game: %s", errorMessage); +``` + +### 2. Structured Messages + +```cpp +// Good: Structured with context +log(LogLevel::Error, "Sprite load failed: file='%s' error='%s'", filename, error); + +// Less useful: Vague messages +log(LogLevel::Error, "Something went wrong"); +``` + +### 3. Performance-Aware Logging + +```cpp +// Good: Conditional expensive operations +#ifdef PIXELROOT32_DEBUG_MODE + log("Entity state: %s", getDetailedEntityState()); +#endif + +// Avoid: Expensive operations in hot paths +log("Physics step: %s", getPhysicsDebugInfo()); // Every frame +``` + +### 4. Consistent Formatting + +```cpp +// Good: Consistent prefix format +log("[Audio] Track loaded: %s", trackName); +log("[Physics] Collision: %d × %d", entityA, entityB); + +// Alternative: Use component-specific loggers +audioLogger.info("Track loaded: %s", trackName); +physicsLogger.info("Collision: %d × %d", entityA, entityB); +``` + +--- + +## Migration from Platform-Specific Logging + +### Before (v1.0.0) + +```cpp +void logPlayerInfo(int x, int y) { +#ifdef ESP32 + Serial.print("[INFO] Player position: "); + Serial.print(x); + Serial.print(", "); + Serial.println(y); +#else + printf("[INFO] Player position: %d, %d\n", x, y); +#endif +} +``` + +### After (v1.1.0) + +```cpp +#include "core/Log.h" +using namespace pixelroot32::core::logging; + +void logPlayerInfo(int x, int y) { + log(LogLevel::Info, "Player position: %d, %d", x, y); + // Or shorthand: + log("Player position: %d, %d", x, y); +} +``` + +--- + +## See Also + +- [Platform Abstractions Overview](../../manual/platform_abstractions/platform_abstractions.md) +- [Platform Memory API](../platform/platform_memory.md) +- [Migration Guide v1.1.0](../../reference/migration_v1.1.0.md) diff --git a/docs/api_reference/core/scene_manager.md b/docs/api_reference/core/scene_manager.md new file mode 100644 index 0000000..1ee52c0 --- /dev/null +++ b/docs/api_reference/core/scene_manager.md @@ -0,0 +1,189 @@ +# SceneManager + +Manages the stack of active scenes for transitions and scene stacking (pause menus, overlays). + +## Description + +The `SceneManager` class provides scene management functionality including scene transitions (replacing) and stacking (push/pop). This is useful for implementing pause menus, dialog overlays, and other temporary scenes that should return to the previous scene when closed. + +## Namespace + +```cpp +namespace pixelroot32::core { + class SceneManager { + // ... + }; +} +``` + +## Inheritance + +- Base class: None (standalone class) +- Used by: `Engine` (manages scene stack) + +## Public Methods + +### void setCurrentScene(Scene* newScene) + +Replaces the current scene with a new one. + +**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) +- The scene's `init()` method will be called automatically +- Safe to call during the game loop + +**Example:** +```cpp +SceneManager sceneManager; +MainMenuScene menuScene; +GameScene gameScene; + +sceneManager.setCurrentScene(&menuScene); // Start with menu +// Later... +sceneManager.setCurrentScene(&gameScene); // Switch to game +``` + +### void pushScene(Scene* newScene) + +Pushes a new scene onto the stack, pausing the previous one. + +**Parameters:** +- `newScene` (`Scene*`): Pointer to the Scene to push onto the stack + +**Notes:** +- The previous scene remains in memory but is not updated or drawn +- Useful for pause menus, dialogs, and temporary overlays +- The new scene's `init()` method will be called automatically + +**Example:** +```cpp +// In game scene, when player pauses +PauseMenuScene pauseMenu; +sceneManager.pushScene(&pauseMenu); // Game scene paused, pause menu active +``` + +### void popScene() + +Removes the top scene from the stack, resuming the previous one. + +**Parameters:** +- None + +**Notes:** +- The top scene is removed and the previous scene becomes active again +- If there's only one scene, this method does nothing +- The resumed scene's `init()` method is NOT called again + +**Example:** +```cpp +// In pause menu, when player resumes +sceneManager.popScene(); // Pause menu removed, game scene resumed +``` + +### std::optional getCurrentScene() const + +Gets the currently active scene, or std::nullopt if no scene is active. + +**Returns:** +- `std::optional`: Optional containing pointer to the current Scene, or std::nullopt if none is set + +**Example:** +```cpp +auto currentScene = sceneManager.getCurrentScene(); +if (currentScene.has_value()) { + Scene* scene = currentScene.value(); + // Use scene... +} +``` + +## Usage Example + +```cpp +#include "core/SceneManager.h" +#include "MyScenes.h" + +class Game { +public: + void init() { + // Start with main menu + sceneManager.setCurrentScene(&mainMenu); + } + + void startGame() { + // Replace menu with game + sceneManager.setCurrentScene(&gameScene); + } + + void pauseGame() { + // Push pause menu on top of game + sceneManager.pushScene(&pauseMenu); + } + + void resumeGame() { + // Pop pause menu, return to game + sceneManager.popScene(); + } + + void showDialog(DialogScene* dialog) { + // Push dialog on top of current scene + sceneManager.pushScene(dialog); + } + + void closeDialog() { + // Pop dialog, return to previous scene + sceneManager.popScene(); + } + +private: + SceneManager sceneManager; + MainMenuScene mainMenu; + GameScene gameScene; + PauseMenuScene pauseMenu; +}; +``` + +## Scene Stack Example + +```cpp +// Initial state: [MainMenu] +sceneManager.setCurrentScene(&mainMenu); + +// Start game: [GameScene] +sceneManager.setCurrentScene(&gameScene); + +// Pause game: [GameScene, PauseMenu] +sceneManager.pushScene(&pauseMenu); + +// Show settings from pause: [GameScene, PauseMenu, SettingsMenu] +sceneManager.pushScene(&settingsMenu); + +// Close settings: [GameScene, PauseMenu] +sceneManager.popScene(); + +// Resume game: [GameScene] +sceneManager.popScene(); + +// Return to menu: [MainMenu] +sceneManager.setCurrentScene(&mainMenu); +``` + +## Performance Considerations + +- **Memory**: Pushed scenes remain in memory, so avoid deep stacks +- **Scene switching**: Fast operation, safe to call every frame if needed +- **Stack depth**: Keep stack shallow (2-3 levels max) for best performance on ESP32 + +## ESP32 Considerations + +- Monitor memory usage when pushing scenes +- Avoid keeping large scenes in the stack +- Consider using scene transitions instead of stacking for memory-constrained scenarios + +## See Also + +- [Scene](scene.md) - Scene class +- [Engine](engine.md) - Main engine class +- [Manual - Scenes and Entities](../../manual/game_development/scenes_and_entities.md) diff --git a/docs/api_reference/graphics/particles.md b/docs/api_reference/graphics/particles.md new file mode 100644 index 0000000..4af5133 --- /dev/null +++ b/docs/api_reference/graphics/particles.md @@ -0,0 +1,347 @@ +# Particle System + +Visual effects system for creating fire, smoke, explosions, and other particle-based effects. + +## Description + +The particle system provides a lightweight, efficient way to create visual effects using small colored pixels. It includes a `ParticleEmitter` entity that manages a pool of particles and several predefined `ParticleConfig` presets for common effects. + +**Note:** The particle system is only available if `PIXELROOT32_ENABLE_PARTICLES=1` + +## Namespace + +```cpp +namespace pixelroot32::graphics { + class ParticleEmitter; + struct ParticleConfig; + namespace ParticlePresets { + // Predefined configurations + } +} +``` + +## ParticleEmitter + +**Inherits:** [Entity](../core/entity.md) + +Manages a pool of particles to create visual effects. + +### Constructors + +#### ParticleEmitter(Vector2 position, const ParticleConfig& cfg) + +Constructs a new particle emitter with specific configuration. + +**Parameters:** + +- `position` (`Vector2`): Initial position of the emitter +- `cfg` (const `ParticleConfig&`): Configuration for particle behavior + +**Example:** + +```cpp +using namespace pixelroot32::graphics; + +ParticleEmitter* fireEffect = new ParticleEmitter( + Vector2(100, 100), + ParticlePresets::Fire +); +scene->addEntity(fireEffect); +``` + +### Public Methods + +#### void burst(Vector2 position, int count) + +Emits a burst of particles from a specific location. + +**Parameters:** + +- `position` (`Vector2`): Position to emit particles from +- `count` (int): Number of particles to emit + +**Example:** + +```cpp +// Explosion at player position +explosionEmitter->burst(Vector2(playerX, playerY), 50); +``` + +#### void setEmitting(bool emitting) + +Enables or disables continuous particle emission. + +**Parameters:** + +- `emitting` (bool): `true` to enable continuous emission, `false` to stop + +**Example:** + +```cpp +// Start emitting smoke +smokeEmitter->setEmitting(true); + +// Stop emitting +smokeEmitter->setEmitting(false); +``` + +#### void setPosition(Vector2 position) + +Sets the emitter's position. + +**Parameters:** + +- `position` (`Vector2`): New position for the emitter + +**Example:** + +```cpp +// Move emitter to follow player +fireEmitter->setPosition(Vector2(playerX, playerY + 10)); +``` + +## ParticleConfig + +Configuration parameters for a particle emitter. + +### Properties + +- **`Color startColor`**: Color at the beginning of the particle's life. +- **`Color endColor`**: Color at the end of the particle's life. +- **`Scalar minSpeed`**: Minimum initial speed in pixels per second. +- **`Scalar maxSpeed`**: Maximum initial speed in pixels per second. +- **`Scalar gravity`**: Y-axis force applied to particles (positive = down). +- **`Scalar friction`**: Velocity damping factor (0.0 = no friction, 1.0 = instant stop). +- **`uint8_t minLife`**: Minimum lifetime in frames/ticks. +- **`uint8_t maxLife`**: Maximum lifetime in frames/ticks. +- **`bool fadeColor`**: If `true`, interpolates color from startColor to endColor over lifetime. +- **`Scalar minAngleDeg`**: Minimum emission angle in degrees (0 = right, 90 = down, 180 = left, 270 = up). +- **`Scalar maxAngleDeg`**: Maximum emission angle in degrees. + +### Example + +```cpp +using namespace pixelroot32::graphics; +using namespace pixelroot32::math; + +ParticleConfig customEffect; +customEffect.startColor = Color::Yellow; +customEffect.endColor = Color::Red; +customEffect.minSpeed = toScalar(50.0f); +customEffect.maxSpeed = toScalar(100.0f); +customEffect.gravity = toScalar(200.0f); // Pull down +customEffect.friction = toScalar(0.95f); // Slight slowdown +customEffect.minLife = 30; // 0.5 seconds at 60 FPS +customEffect.maxLife = 60; // 1 second at 60 FPS +customEffect.fadeColor = true; +customEffect.minAngleDeg = toScalar(0.0f); +customEffect.maxAngleDeg = toScalar(360.0f); // All directions + +ParticleEmitter* emitter = new ParticleEmitter( + Vector2(120, 120), + customEffect +); +``` + +## ParticlePresets + +Namespace containing predefined `ParticleConfig` constants for common effects. + +### Available Presets + +#### Fire + +Upward-moving orange/red particles with gravity, simulating fire. + +**Characteristics:** + +- Colors: Orange → Red +- Direction: Upward (270° ± 30°) +- Speed: Medium +- Gravity: Negative (upward) +- Fade: Yes + +**Example:** + +```cpp +ParticleEmitter* fire = new ParticleEmitter( + Vector2(100, 200), + ParticlePresets::Fire +); +fire->setEmitting(true); // Continuous fire +``` + +#### Explosion + +Fast-moving particles in all directions with gravity, simulating an explosion. + +**Characteristics:** + +- Colors: Yellow → Red +- Direction: All directions (0° - 360°) +- Speed: Fast +- Gravity: Positive (downward) +- Fade: Yes + +**Example:** + +```cpp +ParticleEmitter* explosion = new ParticleEmitter( + Vector2(120, 120), + ParticlePresets::Explosion +); +explosion->burst(Vector2(120, 120), 100); // One-time burst +``` + +#### Sparks + +Fast, short-lived particles with minimal gravity, simulating sparks. + +**Characteristics:** + +- Colors: White → Yellow +- Direction: All directions +- Speed: Very fast +- Gravity: Low +- Fade: Yes +- Lifetime: Short + +**Example:** + +```cpp +ParticleEmitter* sparks = new ParticleEmitter( + Vector2(150, 100), + ParticlePresets::Sparks +); +sparks->burst(Vector2(150, 100), 30); +``` + +#### Smoke + +Slow-moving upward particles with fade, simulating smoke. + +**Characteristics:** + +- Colors: Dark Gray → Light Gray +- Direction: Upward (270° ± 20°) +- Speed: Slow +- Gravity: Negative (upward) +- Fade: Yes +- Lifetime: Long + +**Example:** + +```cpp +ParticleEmitter* smoke = new ParticleEmitter( + Vector2(100, 200), + ParticlePresets::Smoke +); +smoke->setEmitting(true); // Continuous smoke +``` + +#### Dust + +Slow-moving particles in all directions with gravity, simulating dust. + +**Characteristics:** + +- Colors: Brown → Light Gray +- Direction: All directions +- Speed: Slow +- Gravity: Positive (downward) +- Fade: Yes + +**Example:** + +```cpp +ParticleEmitter* dust = new ParticleEmitter( + Vector2(120, 200), + ParticlePresets::Dust +); +dust->burst(Vector2(120, 200), 20); +``` + +## Usage Examples + +### One-Shot Effect (Explosion) + +```cpp +void onEnemyDestroyed(float x, float y) { + ParticleEmitter* explosion = new ParticleEmitter( + Vector2(x, y), + ParticlePresets::Explosion + ); + explosion->burst(Vector2(x, y), 50); + scene->addEntity(explosion); + + // Emitter will auto-cleanup when all particles are dead +} +``` + +### Continuous Effect (Fire) + +```cpp +class Torch : public Entity { +public: + Torch(float x, float y) : Entity(x, y, 8, 16, EntityType::DECORATION) { + fireEmitter = new ParticleEmitter( + Vector2(x + 4, y), // Center of torch + ParticlePresets::Fire + ); + fireEmitter->setEmitting(true); + } + + void update(unsigned long deltaTime) override { + // Update fire position if torch moves + fireEmitter->setPosition(Vector2(x + 4, y)); + } + +private: + ParticleEmitter* fireEmitter; +}; +``` + +### Custom Effect + +```cpp +// Create a custom "magic sparkle" effect +ParticleConfig magicSparkle; +magicSparkle.startColor = Color::Cyan; +magicSparkle.endColor = Color::Purple; +magicSparkle.minSpeed = toScalar(20.0f); +magicSparkle.maxSpeed = toScalar(60.0f); +magicSparkle.gravity = toScalar(-50.0f); // Slight upward +magicSparkle.friction = toScalar(0.98f); +magicSparkle.minLife = 40; +magicSparkle.maxLife = 80; +magicSparkle.fadeColor = true; +magicSparkle.minAngleDeg = toScalar(0.0f); +magicSparkle.maxAngleDeg = toScalar(360.0f); + +ParticleEmitter* magic = new ParticleEmitter( + Vector2(playerX, playerY), + magicSparkle +); +magic->burst(Vector2(playerX, playerY), 30); +``` + +## Performance Considerations + +- **Particle count**: Keep total active particles under 200 for smooth performance on ESP32 +- **Burst size**: Limit burst() count to 50-100 particles per call +- **Continuous emitters**: Use sparingly (2-3 max per scene) +- **Cleanup**: Emitters automatically clean up when all particles are dead + +## ESP32 Considerations + +- Particles use minimal memory (8-12 bytes each) +- Rendering is optimized (single pixel per particle) +- Avoid creating/destroying emitters every frame +- Reuse emitters when possible + +## See Also + +- [Entity](../core/entity.md) - Base entity class +- [Renderer](renderer.md) - Rendering system +- [Color](color.md) - Color system +- [Manual - Particles and Effects](../../manual/advanced_graphics/particles_and_effects.md) diff --git a/docs/api_reference/graphics/renderer.md b/docs/api_reference/graphics/renderer.md index e5053c0..28cac86 100644 --- a/docs/api_reference/graphics/renderer.md +++ b/docs/api_reference/graphics/renderer.md @@ -221,7 +221,7 @@ Draws text centered horizontally at a given Y coordinate using a specific font. - `size` (uint8_t): Text size - `font` (const Font*): Pointer to the font to use. If `nullptr`, uses the default font -### void drawFilledCircle(int x, int y, int radius, Color color) +### void drawFilledCircle(int x, int y, int radius, uint16_t color) Draws a filled circle. @@ -230,15 +230,15 @@ Draws a filled circle. - `x` (int): Center X coordinate - `y` (int): Center Y coordinate - `radius` (int): Radius of the circle in pixels -- `color` (`Color`): Fill color +- `color` (uint16_t): Fill color in RGB565 format **Example:** ```cpp -renderer.drawFilledCircle(64, 64, 20, Color::Red); +renderer.drawFilledCircle(64, 64, 20, renderer.color565(255, 0, 0)); ``` -### void drawCircle(int x, int y, int radius, Color color) +### void drawCircle(int x, int y, int radius, uint16_t color) Draws a circle outline. @@ -247,15 +247,15 @@ Draws a circle outline. - `x` (int): Center X coordinate - `y` (int): Center Y coordinate - `radius` (int): Radius of the circle in pixels -- `color` (`Color`): Outline color +- `color` (uint16_t): Outline color in RGB565 format **Example:** ```cpp -renderer.drawCircle(64, 64, 20, Color::White); +renderer.drawCircle(64, 64, 20, renderer.color565(255, 255, 255)); ``` -### void drawRectangle(int x, int y, int width, int height, Color color) +### void drawRectangle(int x, int y, int width, int height, uint16_t color) Draws a rectangle outline. @@ -265,15 +265,15 @@ Draws a rectangle outline. - `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 +- `color` (uint16_t): Outline color in RGB565 format **Example:** ```cpp -renderer.drawRectangle(10, 10, 100, 50, Color::Blue); +renderer.drawRectangle(10, 10, 100, 50, renderer.color565(0, 0, 255)); ``` -### void drawFilledRectangle(int x, int y, int width, int height, Color color) +### void drawFilledRectangle(int x, int y, int width, int height, uint16_t color) Draws a filled rectangle. @@ -283,15 +283,15 @@ Draws a filled rectangle. - `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 +- `color` (uint16_t): Fill color in RGB565 format **Example:** ```cpp -renderer.drawFilledRectangle(10, 10, 100, 50, Color::Green); +renderer.drawFilledRectangle(10, 10, 100, 50, renderer.color565(0, 255, 0)); ``` -### void drawLine(int x1, int y1, int x2, int y2, Color color) +### void drawLine(int x1, int y1, int x2, int y2, uint16_t color) Draws a line between two points. @@ -301,15 +301,15 @@ Draws a line between two points. - `y1` (int): Start Y coordinate - `x2` (int): End X coordinate - `y2` (int): End Y coordinate -- `color` (`Color`): Line color +- `color` (uint16_t): Line color in RGB565 format **Example:** ```cpp -renderer.drawLine(0, 0, 128, 128, Color::White); +renderer.drawLine(0, 0, 128, 128, renderer.color565(255, 255, 255)); ``` -### void drawPixel(int x, int y, Color color) +### void drawPixel(int x, int y, uint16_t color) Draws a single pixel. @@ -317,7 +317,7 @@ Draws a single pixel. - `x` (int): X coordinate - `y` (int): Y coordinate -- `color` (`Color`): Pixel color +- `color` (uint16_t): Pixel color in RGB565 format **Performance Notes:** @@ -327,7 +327,27 @@ Draws a single pixel. **Example:** ```cpp -renderer.drawPixel(64, 64, Color::Red); +renderer.drawPixel(64, 64, renderer.color565(255, 0, 0)); +``` + +### void drawBitmap(int x, int y, int width, int height, const uint8_t *bitmap, uint16_t color) + +Draws a bitmap image. + +**Parameters:** + +- `x` (int): Top-left X coordinate +- `y` (int): Top-left Y coordinate +- `width` (int): Width of the bitmap in pixels +- `height` (int): Height of the bitmap in pixels +- `bitmap` (const uint8_t*): Pointer to bitmap data +- `color` (uint16_t): Color for "on" pixels in RGB565 format + +**Example:** + +```cpp +const uint8_t myBitmap[] = { /* bitmap data */ }; +renderer.drawBitmap(10, 10, 16, 16, myBitmap, renderer.color565(255, 255, 255)); ``` ### void drawSprite(const Sprite& sprite, int x, int y, Color color, bool flipX = false) @@ -469,6 +489,8 @@ Draws a 2bpp tilemap. Available when `PIXELROOT32_ENABLE_2BPP_SPRITES` is define - `originX` (int): X coordinate - `originY` (int): Y coordinate +**Multi-palette:** If `map.paletteIndices` is non-null, each cell can use a different background palette slot (0–7); otherwise all cells use slot 0. Register palettes with `pixelroot32::graphics::setBackgroundCustomPaletteSlot(slot, palette)` before drawing. + ### void drawTileMap(const TileMap4bpp& map, int originX, int originY) Draws a 4bpp tilemap. Available when `PIXELROOT32_ENABLE_4BPP_SPRITES` is defined. @@ -479,6 +501,8 @@ Draws a 4bpp tilemap. Available when `PIXELROOT32_ENABLE_4BPP_SPRITES` is define - `originX` (int): X coordinate - `originY` (int): Y coordinate +**Multi-palette:** If `map.paletteIndices` is non-null, each cell can use a different background palette slot (0–7); otherwise all cells use slot 0. Register palettes with `pixelroot32::graphics::setBackgroundCustomPaletteSlot(slot, palette)` before drawing. + **Performance Notes:** - Very efficient for rendering large backgrounds @@ -634,8 +658,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**: 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). +- **Tilemaps**: Draws a full tilemap with automatic viewport culling. For 2bpp/4bpp, supports optional per-cell palette slots via `paletteIndices` (see [Color Palettes](../../manual/advanced_graphics/color_palettes.md) — Background palette slot bank). +- **Sprites 2bpp/4bpp**: Optimised for ESP32 (IRAM + 16-bit access). ## ESP32 Considerations diff --git a/docs/api_reference/index.md b/docs/api_reference/index.md new file mode 100644 index 0000000..80dbae4 --- /dev/null +++ b/docs/api_reference/index.md @@ -0,0 +1,137 @@ +# API Reference + +Complete reference documentation for the PixelRoot32 Game Engine API. + +## Overview + +The PixelRoot32 Game Engine is organized into several modules, each providing specific functionality for game development on ESP32 and native platforms. + +## Core Modules + +### [Math Module](math/math_module.md) + +Platform-agnostic numerical abstraction layer with `Scalar` type and `Vector2` class for physics and positioning. + +- [Scalar & Vector2](math/math_module.md) - Fundamental numeric types + +### [Core Module](core/engine.md) + +Fundamental building blocks including the main application loop, entity management, and scene organization. + +- [Engine](core/engine.md) - Main engine class and game loop +- [Entity](core/entity.md) - Base class for all game objects +- [Actor](core/actor.md) - Base class for collidable objects +- [PhysicsActor](core/physics_actor.md) - Base class for physics-enabled bodies +- [Scene](core/scene.md) - Game level/screen management +- [InputManager](core/input_manager.md) - Input handling system +- [InputConfig](core/input_config.md) - Input configuration +- [PlatformCapabilities](core/platform_capabilities.md) - Hardware capability detection +- [Global Configuration](core/global_config.md) - Build flags and constants + +### [Graphics Module](graphics/renderer.md) + +Everything related to drawing to the screen, including text, shapes, bitmaps, and particle effects. + +- [Renderer](graphics/renderer.md) - High-level rendering system +- [Sprite](graphics/sprite.md) - Sprite structures (1bpp, 2bpp, 4bpp, multi-layer) +- [TileMap](graphics/tilemap.md) - Tile-based backgrounds +- [Color](graphics/color.md) - Color palettes and management +- [Font](graphics/font.md) - Bitmap font system +- [Camera2D](graphics/camera2d.md) - 2D camera for scrolling +- [DisplayConfig](graphics/display_config.md) - Display configuration + +### [Physics Module](physics/collision_system.md) + +High-performance "Flat Solver" optimized for microcontrollers with collision detection and resolution. + +- [CollisionSystem](physics/collision_system.md) - Central physics system +- [StaticActor](physics/static_actor.md) - Immovable bodies (walls, floors) +- [KinematicActor](physics/kinematic_actor.md) - Manually moved bodies (players, platforms) +- [RigidActor](physics/rigid_actor.md) - Fully simulated bodies (props, debris) +- [Collision Types](physics/collision_types.md) - Collision primitives and helpers + +### [Audio Module](audio/audio_engine.md) + +NES-like audio system with Pulse, Triangle, and Noise channels, plus melody subsystem. + +- [AudioEngine](audio/audio_engine.md) - Core audio generation and playback +- [MusicPlayer](audio/music_player.md) - Background music sequencer +- [Audio Types](audio/audio_types.md) - Audio events, notes, and tracks +- [AudioConfig](audio/audio_config.md) - Audio configuration + +**Note:** Audio module is only available if `PIXELROOT32_ENABLE_AUDIO=1` + +### [UI Module](ui/ui_element.md) + +Classes for creating user interfaces with buttons, labels, and layouts. + +- [UIElement](ui/ui_element.md) - Base class for UI components +- [UIButton](ui/ui_button.md) - Clickable button +- [UILabel](ui/ui_label.md) - Text label +- [UILayout](ui/ui_layout.md) - Base layout container +- [Layout Types](ui/ui_layouts/) - Vertical, Horizontal, Grid, Anchor layouts +- [UIPanel](ui/ui_panel.md) - Visual container with background/border +- [UIPaddingContainer](ui/ui_padding_container.md) - Padding wrapper + +**Note:** UI module is only available if `PIXELROOT32_ENABLE_UI_SYSTEM=1` + +## Quick Navigation + +### By Category + +**Getting Started** + +- [Engine](core/engine.md) - Start here +- [Scene](core/scene.md) - Create your first scene +- [Entity](core/entity.md) - Understand game objects + +**Rendering** + +- [Renderer](graphics/renderer.md) - Drawing API +- [Sprite](graphics/sprite.md) - Sprite formats +- [Color](graphics/color.md) - Color system + +**Physics** + +- [CollisionSystem](physics/collision_system.md) - Physics overview +- [KinematicActor](physics/kinematic_actor.md) - Player movement +- [RigidActor](physics/rigid_actor.md) - Dynamic objects + +**Audio** + +- [AudioEngine](audio/audio_engine.md) - Sound effects +- [MusicPlayer](audio/music_player.md) - Background music + +**User Interface** + +- [UIButton](ui/ui_button.md) - Interactive buttons +- [UILayout](ui/ui_layout.md) - Layout system + +## Modular Compilation + +The engine supports modular compilation to reduce binary size and memory usage. You can disable unused subsystems using build flags: + +```ini +build_flags = + -D PIXELROOT32_ENABLE_AUDIO=0 # Disable audio + -D PIXELROOT32_ENABLE_PHYSICS=0 # Disable physics + -D PIXELROOT32_ENABLE_UI_SYSTEM=0 # Disable UI + -D PIXELROOT32_ENABLE_PARTICLES=0 # Disable particles +``` + +See [Global Configuration](core/global_config.md) for more details. + +## Platform Support + +The engine supports multiple platforms with consistent API: + +- **ESP32** (Classic, S2, S3, C3) - Primary target +- **Native (SDL2)** - PC development and testing + +Platform-specific optimizations are handled automatically. See [Platform Compatibility Guide](../manual/optimization/platform_compatibility.md) for details. + +## See Also + +- [Getting Started Guide](../getting_started/index.md) +- [Manual](../manual/index.md) +- [Examples](../examples/index.md) diff --git a/docs/api_reference/platform/platform_memory.md b/docs/api_reference/platform/platform_memory.md new file mode 100644 index 0000000..3888933 --- /dev/null +++ b/docs/api_reference/platform/platform_memory.md @@ -0,0 +1,367 @@ +# Platform Memory API Reference + +The Platform Memory API provides unified access to Flash/PROGMEM memory on ESP32 and regular RAM on native platforms. This eliminates the need for manual `#ifdef` blocks in user code. + +--- + +## Including the Header + +```cpp +#include "platforms/PlatformMemory.h" +``` + +--- + +## Core Macros + +### Data Attributes + +#### `PIXELROOT32_FLASH_ATTR` + +Attribute for storing constant data in Flash memory on ESP32 platforms. Has no effect on native platforms. + +```cpp +const char MY_STRING[] PIXELROOT32_FLASH_ATTR = "Hello, World!"; +const uint16_t COLOR_PALETTE[] PIXELROOT32_FLASH_ATTR = {0x0000, 0xF800, 0x07E0}; +``` + +**Platform Behavior:** +- **ESP32**: Places data in PROGMEM (Flash memory) +- **Native**: Standard storage in RAM + +--- + +### String Operations + +#### `PIXELROOT32_STRCMP_P` + +Compare a RAM string with a Flash string. + +```cpp +int PIXELROOT32_STRCMP_P(char* dest, const char* src); +``` + +**Parameters:** +- `dest`: Destination string in RAM +- `src`: Source string in Flash (marked with `PIXELROOT32_FLASH_ATTR`) + +**Returns:** +- `0` if strings are equal +- `< 0` if dest < src +- `> 0` if dest > src + +**Example:** +```cpp +const char FLASH_STRING[] PIXELROOT32_FLASH_ATTR = "Hello"; +char buffer[32]; + +if (PIXELROOT32_STRCMP_P(buffer, FLASH_STRING) == 0) { + // Strings match +} +``` + +--- + +### Memory Copy Operations + +#### `PIXELROOT32_MEMCPY_P` + +Copy data from Flash memory to RAM. + +```cpp +void PIXELROOT32_MEMCPY_P(void* dest, const void* src, size_t size); +``` + +**Parameters:** +- `dest`: Destination buffer in RAM +- `src`: Source data in Flash +- `size`: Number of bytes to copy + +**Example:** +```cpp +const uint8_t SPRITE_DATA[] PIXELROOT32_FLASH_ATTR = {1, 2, 3, 4, 5}; +uint8_t localBuffer[5]; + +PIXELROOT32_MEMCPY_P(localBuffer, SPRITE_DATA, sizeof(localBuffer)); +``` + +--- + +### Data Reading Operations + +#### `PIXELROOT32_READ_BYTE_P` + +Read an 8-bit value from Flash memory. + +```cpp +uint8_t PIXELROOT32_READ_BYTE_P(const uint8_t* addr); +``` + +**Parameters:** +- `addr`: Address in Flash memory + +**Returns:** 8-bit value + +**Example:** +```cpp +const uint8_t DATA[] PIXELROOT32_FLASH_ATTR = {0x12, 0x34, 0x56}; +uint8_t value = PIXELROOT32_READ_BYTE_P(&DATA[1]); // Returns 0x34 +``` + +#### `PIXELROOT32_READ_WORD_P` + +Read a 16-bit value from Flash memory. + +```cpp +uint16_t PIXELROOT32_READ_WORD_P(const uint16_t* addr); +``` + +**Parameters:** +- `addr`: Address in Flash memory + +**Returns:** 16-bit value + +**Example:** +```cpp +const uint16_t WORDS[] PIXELROOT32_FLASH_ATTR = {0x1234, 0x5678}; +uint16_t value = PIXELROOT32_READ_WORD_P(&WORDS[0]); // Returns 0x1234 +``` + +#### `PIXELROOT32_READ_DWORD_P` + +Read a 32-bit value from Flash memory. + +```cpp +uint32_t PIXELROOT32_READ_DWORD_P(const uint32_t* addr); +``` + +**Parameters:** +- `addr`: Address in Flash memory + +**Returns:** 32-bit value + +**Example:** +```cpp +const uint32_t DWORDS[] PIXELROOT32_FLASH_ATTR = {0x12345678, 0xABCDEF00}; +uint32_t value = PIXELROOT32_READ_DWORD_P(&DWORDS[0]); // Returns 0x12345678 +``` + +#### `PIXELROOT32_READ_FLOAT_P` + +Read a float value from Flash memory. + +```cpp +float PIXELROOT32_READ_FLOAT_P(const float* addr); +``` + +**Parameters:** +- `addr`: Address in Flash memory + +**Returns:** Float value + +**Example:** +```cpp +const float FLOATS[] PIXELROOT32_FLASH_ATTR = {3.14159f, 2.71828f}; +float value = PIXELROOT32_READ_FLOAT_P(&FLOATS[0]); // Returns 3.14159f +``` + +#### `PIXELROOT32_READ_PTR_P` + +Read a pointer value from Flash memory. + +```cpp +void* PIXELROOT32_READ_PTR_P(const void* const* addr); +``` + +**Parameters:** +- `addr`: Address in Flash memory containing a pointer + +**Returns:** Pointer value + +**Example:** +```cpp +void function1() { /* ... */ } +void function2() { /* ... */ } + +void (*FUNCTION_TABLE[])(void) PIXELROOT32_FLASH_ATTR = { + function1, function2 +}; + +void callFunction(int index) { + void (*func)() = PIXELROOT32_READ_PTR_P(&FUNCTION_TABLE[index]); + if (func) { + func(); + } +} +``` + +--- + +## Usage Patterns + +### Large Lookup Tables + +```cpp +// Efficient storage for large data tables +const uint16_t COLOR_PALETTE[] PIXELROOT32_FLASH_ATTR = { + 0x0000, 0xF800, 0x07E0, 0xFFE0, // Black, Red, Green, Yellow + 0x001F, 0xF81F, 0x07FF, 0xFFFF, // Blue, Magenta, Cyan, White + // ... more colors +}; + +uint16_t getColor(int index) { + return PIXELROOT32_READ_WORD_P(&COLOR_PALETTE[index]); +} +``` + +### Sprite Data + +```cpp +// Sprite frame data stored efficiently +const uint8_t PLAYER_SPRITE[] PIXELROOT32_FLASH_ATTR = { + // Frame 0 + 0x00, 0x7E, 0x81, 0x81, 0x81, 0x7E, + // Frame 1 + 0x00, 0x7E, 0xBD, 0xBD, 0xBD, 0x7E, + // ... more frames +}; + +class Sprite { +private: + const uint8_t* data; + int frameWidth; + +public: + Sprite(const uint8_t* spriteData, int width) + : data(spriteData), frameWidth(width) {} + + uint8_t getPixel(int frame, int x, int y) const { + int index = frame * frameWidth + y * frameWidth + x; + return PIXELROOT32_READ_BYTE_P(&data[index]); + } +}; +``` + +### String Tables + +```cpp +// Multiple strings stored efficiently +const char* const STRING_TABLE[] PIXELROOT32_FLASH_ATTR = { + "Start Game", + "Settings", + "Exit", + "High Scores" +}; + +const char* getString(int index) { + return reinterpret_cast(PIXELROOT32_READ_PTR_P(&STRING_TABLE[index])); +} + +void drawMenu() { + for (int i = 0; i < 4; i++) { + const char* text = getString(i); + // Draw text... + } +} +``` + +--- + +## Performance Considerations + +### ESP32 Platform + +- **Flash Access**: Slower than RAM access but saves significant RAM +- **Bulk Operations**: `PIXELROOT32_MEMCPY_P` is more efficient than individual reads +- **Cache Considerations**: Frequently accessed data may benefit from RAM caching + +### Native Platform + +- **Zero Overhead**: All macros resolve to standard memory operations +- **Direct Access**: Same performance as regular memory access +- **No Optimization Needed**: Code works efficiently without modification + +--- + +## Best Practices + +### 1. Use for Large Constant Data + +```cpp +// Good for large tables +const uint16_t WAVE_TABLE[1024] PIXELROOT32_FLASH_ATTR; + +// Not necessary for small constants +const int MAX_ENEMIES = 10; // Regular variable is fine +``` + +### 2. Bulk Operations When Possible + +```cpp +// Better: Copy entire block +uint8_t frameBuffer[64]; +PIXELROOT32_MEMCPY_P(frameBuffer, &SPRITE_DATA[frameIndex * 64], 64); + +// Less efficient: Individual reads +for (int i = 0; i < 64; i++) { + frameBuffer[i] = PIXELROOT32_READ_BYTE_P(&SPRITE_DATA[frameIndex * 64 + i]); +} +``` + +### 3. Cache Frequently Accessed Data + +```cpp +class Animation { +private: + uint8_t currentFrame[64]; // Cache in RAM + +public: + void setFrame(int frameIndex) { + // Load from Flash once + PIXELROOT32_MEMCPY_P(currentFrame, &ANIMATION_DATA[frameIndex * 64], 64); + } + + uint8_t getPixel(int x, int y) const { + return currentFrame[y * 8 + x]; // Fast RAM access + } +}; +``` + +### 4. Consistent Usage + +```cpp +// Use unified macros throughout your codebase +const char* getString(int index) { + return reinterpret_cast(PIXELROOT32_READ_PTR_P(&STRING_TABLE[index])); +} + +// Avoid mixing old and new patterns +#ifdef ESP32 + return pgm_read_word(&DATA[i]); // Don't do this +#else + return DATA[i]; +#endif +``` + +--- + +## Migration from Legacy APIs + +| Legacy API | New Unified API | +|------------|-----------------| +| `PROGMEM` | `PIXELROOT32_FLASH_ATTR` | +| `strcmp_P` | `PIXELROOT32_STRCMP_P` | +| `memcpy_P` | `PIXELROOT32_MEMCPY_P` | +| `pgm_read_byte` | `PIXELROOT32_READ_BYTE_P` | +| `pgm_read_word` | `PIXELROOT32_READ_WORD_P` | +| `pgm_read_dword` | `PIXELROOT32_READ_DWORD_P` | +| `pgm_read_float` | `PIXELROOT32_READ_FLOAT_P` | +| `pgm_read_ptr` | `PIXELROOT32_READ_PTR_P` | + +--- + +## See Also + +- [Platform Abstractions Overview](../../manual/platform_abstractions/platform_abstractions.md) +- [Logging API Reference](../core/logging.md) +- [Migration Guide v1.1.0](../../reference/migration_v1.1.0.md) diff --git a/docs/api_reference/ui/ui_padding_container.md b/docs/api_reference/ui/ui_padding_container.md new file mode 100644 index 0000000..d0cd21e --- /dev/null +++ b/docs/api_reference/ui/ui_padding_container.md @@ -0,0 +1,240 @@ +# 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. It's useful for adding spacing to individual elements or nesting layouts with custom padding. Unlike layouts, it doesn't reorganize elements—it simply adjusts the child's position based on padding values. + +**Note:** UI module is only available if `PIXELROOT32_ENABLE_UI_SYSTEM=1` + +## Namespace + +```cpp +namespace pixelroot32::ui { + class UIPaddingContainer : public UIElement { + // ... + }; +} +``` + +## Inheritance + +- Base class: [UIElement](ui_element.md) +- Inherits from: `Entity` + +## Constructors + +### UIPaddingContainer(float x, float y, float w, float h) + +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:** +```cpp +UIPaddingContainer* container = new UIPaddingContainer(10, 10, 200, 100); +scene->addEntity(container); +``` + +## Public Methods + +### void setChild(UIElement* element) + +Sets the child element to wrap. + +**Parameters:** +- `element` (`UIElement*`): Pointer to the child element. Can be `nullptr` to clear the child. + +**Notes:** +- The child's position will be adjusted based on padding +- Only one child is supported + +**Example:** +```cpp +UIPaddingContainer* container = new UIPaddingContainer(10, 10, 200, 100); +UIButton* button = new UIButton("Click Me", 4, 0, 0, 100, 30, []() {}); +container->setChild(button); +``` + +### UIElement* getChild() const + +Gets the child element. + +**Returns:** +- `UIElement*`: Pointer to the child element, or `nullptr` if none is set + +### void setPadding(float p) + +Sets uniform padding on all sides. + +**Parameters:** +- `p` (float): Padding value in pixels for all sides + +**Example:** +```cpp +container->setPadding(10); // 10 pixels on all sides +``` + +### void setPadding(float left, float right, float top, float bottom) + +Sets asymmetric padding for each side. + +**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 + +**Example:** +```cpp +container->setPadding(5, 15, 10, 10); // Different padding per side +``` + +### float getPaddingLeft() const + +Gets the left padding. + +**Returns:** +- `float`: Left padding in pixels + +### float getPaddingRight() const + +Gets the right padding. + +**Returns:** +- `float`: Right padding in pixels + +### float getPaddingTop() const + +Gets the top padding. + +**Returns:** +- `float`: Top padding in pixels + +### float getPaddingBottom() const + +Gets the bottom padding. + +**Returns:** +- `float`: Bottom padding in pixels + +## Usage Examples + +### Basic Padding + +```cpp +// Create a container with uniform padding +UIPaddingContainer* container = new UIPaddingContainer(10, 10, 200, 100); +container->setPadding(10); // 10 pixels on all sides + +UIButton* button = new UIButton("Click Me", 4, 0, 0, 180, 80, []() { + // Button callback +}); +container->setChild(button); +container->setRenderLayer(2); +scene->addEntity(container); + +// Button will be positioned at (20, 20) with size (180, 80) +// Container is at (10, 10), padding adds 10 pixels offset +``` + +### Asymmetric Padding + +```cpp +// Create a container with different padding per side +UIPaddingContainer* container = new UIPaddingContainer(10, 10, 200, 100); +container->setPadding(5, 15, 10, 10); // left, right, top, bottom + +UILabel* label = new UILabel("Info", 0, 0, Color::White, 1); +container->setChild(label); +scene->addEntity(container); + +// Label will be positioned at (15, 20) +// Container is at (10, 10), left padding (5) + top padding (10) +``` + +### Nested Layouts with Padding + +```cpp +// Create a padding container with a layout inside +UIPaddingContainer* wrapper = new UIPaddingContainer(0, 0, 320, 240); +wrapper->setPadding(20); // 20 pixels margin around screen edges + +UIVerticalLayout* innerLayout = new UIVerticalLayout(0, 0, 280, 200); +innerLayout->setPadding(10); +innerLayout->setSpacing(5); + +// Add buttons to layout +for (int i = 0; i < 5; i++) { + UIButton* btn = new UIButton("Button " + std::to_string(i), 4, 0, 0, 260, 30, []() {}); + innerLayout->addElement(btn); +} + +wrapper->setChild(innerLayout); +scene->addEntity(wrapper); + +// Layout will be positioned at (20, 20) with size (280, 200) +// Buttons inside layout will have additional 10 pixels padding from layout +``` + +### Padding Around Panel + +```cpp +// Add padding around a panel +UIPaddingContainer* container = new UIPaddingContainer(0, 0, 240, 240); +container->setPadding(30); + +UIPanel* panel = new UIPanel(0, 0, 180, 180); +panel->setBackgroundColor(Color::Black); +panel->setBorderColor(Color::White); +panel->setBorderWidth(2); + +UIVerticalLayout* content = new UIVerticalLayout(0, 0, 180, 180); +content->setPadding(10); +// ... add elements to content ... +panel->setChild(content); + +container->setChild(panel); +scene->addEntity(container); + +// Panel will be centered with 30 pixels margin from screen edges +``` + +## Behavior + +The container calculates the child's position as: +``` +childX = containerX + paddingLeft +childY = containerY + paddingTop +``` + +The available space for the child is: +``` +childWidth = containerWidth - paddingLeft - paddingRight +childHeight = containerHeight - paddingTop - paddingBottom +``` + +## Performance Considerations + +- **No reflow**: The container doesn't reorganize elements, only adjusts position +- **Automatic updates**: Child position is updated when container moves +- **Low overhead**: Minimal performance impact, ideal for ESP32 +- **Efficient**: No additional rendering, just position calculation + +## ESP32 Considerations + +- Very lightweight (no extra draw calls) +- Safe to use multiple containers +- Ideal for creating margins and spacing + +## See Also + +- [UIElement](ui_element.md) - Base UI element class +- [UILayout](ui_layout.md) - Layout containers +- [UIPanel](ui_panel.md) - Visual container with background/border +- [Manual - User Interface](../../manual/game_development/user_interface.md) diff --git a/docs/api_reference/ui/ui_panel.md b/docs/api_reference/ui/ui_panel.md new file mode 100644 index 0000000..2f35b0f --- /dev/null +++ b/docs/api_reference/ui/ui_panel.md @@ -0,0 +1,261 @@ +# UIPanel + +Visual container that draws a background and border around a child element. + +## Description + +`UIPanel` provides a retro-style window/panel appearance for UI elements. It draws a filled background rectangle and an optional border, then renders its child element on top. This is useful for creating dialogs, menus, and information panels with a consistent visual style. + +**Note:** UI module is only available if `PIXELROOT32_ENABLE_UI_SYSTEM=1` + +## Namespace + +```cpp +namespace pixelroot32::ui { + class UIPanel : public UIElement { + // ... + }; +} +``` + +## Inheritance + +- Base class: [UIElement](ui_element.md) +- Inherits from: `Entity` + +## Constructors + +### UIPanel(float x, float y, float w, float h) + +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:** + +```cpp +UIPanel* dialog = new UIPanel(50, 50, 220, 140); +scene->addEntity(dialog); +``` + +## Public Methods + +### void setChild(UIElement* element) + +Sets the child element to wrap (typically a UILayout). + +**Parameters:** + +- `element` (`UIElement*`): Pointer to the child element. Can be `nullptr` to clear the child. + +**Notes:** + +- The child's position is automatically updated to match the panel's position +- Only one child is supported (use a layout to contain multiple elements) + +**Example:** + +```cpp +UIPanel* panel = new UIPanel(50, 50, 220, 140); +UIVerticalLayout* content = new UIVerticalLayout(0, 0, 220, 140); +// ... add elements to content ... +panel->setChild(content); +``` + +### UIElement* getChild() const + +Gets the child element. + +**Returns:** + +- `UIElement*`: Pointer to the child element, or `nullptr` if none is set + +### void setBackgroundColor(Color color) + +Sets the background color. + +**Parameters:** + +- `color` (`Color`): Background color. Use `Color::Transparent` to disable background drawing. + +**Example:** + +```cpp +panel->setBackgroundColor(Color::Black); +panel->setBackgroundColor(Color::Transparent); // No background +``` + +### Color getBackgroundColor() const + +Gets the background color. + +**Returns:** + +- `Color`: Current background color + +### void setBorderColor(Color color) + +Sets the border color. + +**Parameters:** + +- `color` (`Color`): Border color. Use `Color::Transparent` to disable border drawing. + +**Example:** + +```cpp +panel->setBorderColor(Color::White); +panel->setBorderColor(Color::Transparent); // No border +``` + +### Color getBorderColor() const + +Gets the border color. + +**Returns:** + +- `Color`: Current border color + +### void setBorderWidth(uint8_t width) + +Sets the border width in pixels. + +**Parameters:** + +- `width` (uint8_t): Border width. Set to 0 to disable border. + +**Example:** + +```cpp +panel->setBorderWidth(2); // 2-pixel border +panel->setBorderWidth(0); // No border +``` + +### uint8_t getBorderWidth() const + +Gets the border width. + +**Returns:** + +- `uint8_t`: Current border width in pixels + +## Usage Examples + +### Basic Dialog Panel + +```cpp +// Create a dialog panel +UIPanel* dialog = new UIPanel(50, 50, 220, 140); +dialog->setBackgroundColor(Color::Black); +dialog->setBorderColor(Color::White); +dialog->setBorderWidth(2); +dialog->setRenderLayer(2); +scene->addEntity(dialog); + +// Add content layout inside the panel +UIVerticalLayout* content = new UIVerticalLayout(0, 0, 220, 140); +content->setPadding(10); +content->setSpacing(5); + +UILabel* title = new UILabel("Dialog Title", 0, 0, Color::White, 2); +UIButton* okBtn = new UIButton("OK", 4, 0, 0, 100, 20, []() { + // OK button callback +}); + +content->addElement(title); +content->addElement(okBtn); +dialog->setChild(content); +``` + +### Transparent Panel (Border Only) + +```cpp +// Create a panel with border but no background +UIPanel* borderPanel = new UIPanel(10, 10, 200, 100); +borderPanel->setBackgroundColor(Color::Transparent); // No background +borderPanel->setBorderColor(Color::Cyan); +borderPanel->setBorderWidth(1); +scene->addEntity(borderPanel); +``` + +### Nested Panels + +```cpp +// Outer panel +UIPanel* outerPanel = new UIPanel(20, 20, 280, 200); +outerPanel->setBackgroundColor(Color::DarkGray); +outerPanel->setBorderColor(Color::White); +outerPanel->setBorderWidth(2); + +// Inner panel +UIPanel* innerPanel = new UIPanel(30, 30, 260, 180); +innerPanel->setBackgroundColor(Color::Black); +innerPanel->setBorderColor(Color::LightGray); +innerPanel->setBorderWidth(1); + +// Add content to inner panel +UIVerticalLayout* content = new UIVerticalLayout(0, 0, 260, 180); +// ... add elements ... +innerPanel->setChild(content); + +// Add inner panel to outer panel +outerPanel->setChild(innerPanel); + +scene->addEntity(outerPanel); +``` + +### Information Panel + +```cpp +// Create an info panel with stats +UIPanel* statsPanel = new UIPanel(10, 10, 100, 80); +statsPanel->setBackgroundColor(Color::DarkBlue); +statsPanel->setBorderColor(Color::Cyan); +statsPanel->setBorderWidth(1); +statsPanel->setRenderLayer(2); + +UIVerticalLayout* stats = new UIVerticalLayout(0, 0, 100, 80); +stats->setPadding(5); +stats->setSpacing(3); + +stats->addElement(new UILabel("HP: 100", 0, 0, Color::White, 1)); +stats->addElement(new UILabel("MP: 50", 0, 0, Color::White, 1)); +stats->addElement(new UILabel("Level: 5", 0, 0, Color::White, 1)); + +statsPanel->setChild(stats); +scene->addEntity(statsPanel); +``` + +## Rendering Order + +The panel renders in this order: + +1. Background (filled rectangle) +2. Border (4 filled rectangles for each side) +3. Child element (layout, button, label, etc.) + +## Performance Considerations + +- **Efficient rendering**: Background and border use simple filled rectangles +- **Transparent support**: Set colors to `Color::Transparent` or border width to 0 to skip drawing +- **Child positioning**: Child position is automatically updated when panel moves +- **Low overhead**: Minimal performance impact, ideal for ESP32 + +## ESP32 Considerations + +- Panels are very efficient (simple rectangle drawing) +- Use sparingly for nested panels (each adds draw calls) +- Consider using transparent backgrounds for overlays + +## See Also + +- [UIElement](ui_element.md) - Base UI element class +- [UILayout](ui_layout.md) - Layout containers +- [UIPaddingContainer](ui_padding_container.md) - Padding wrapper +- [Color](../graphics/color.md) - Color system +- [Manual - User Interface](../../manual/game_development/user_interface.md) diff --git a/docs/examples/index.md b/docs/examples/index.md new file mode 100644 index 0000000..471fe4b --- /dev/null +++ b/docs/examples/index.md @@ -0,0 +1,361 @@ +# Examples + +PixelRoot32 includes a comprehensive collection of game examples and demos that demonstrate various engine features and best practices. These examples are designed to help you learn by studying working code. + +## Example Repository + +All examples are available in the [PixelRoot32 Game Samples](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Samples) repository. + +## Available Examples + +### Complete Games + +#### [Space Invaders](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Samples/tree/main/src/examples/Games/SpaceInvaders) + +Classic arcade shooter with enemies, projectiles, bunkers, and audio. + +**Features demonstrated:** + +- Collision layers and masks +- Object pooling for projectiles +- Audio integration (music + sound effects) +- State management +- Grid-based enemy formation +- Background rendering (starfield or tilemap) + +**Complexity:** Intermediate +**Recommended for:** Learning collision systems and audio + +--- + +#### [Metroidvania](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Samples/tree/main/src/examples/Games/Metroidvania) + +2D platformer with multi-layer 4bpp tilemap and tile-based collision. + +**Features demonstrated:** + +- 4bpp sprites and tilemaps +- Tile-based collision detection +- Multi-layer rendering +- Player movement and physics +- Viewport culling optimization +- Stairs and platform mechanics + +**Complexity:** Advanced +**Recommended for:** Learning advanced graphics and tile-based games +**Note:** Requires `PIXELROOT32_ENABLE_4BPP_SPRITES=1` + +**Assets:** Uses [Tiny Metroidvania 8x8](https://kenmi-art.itch.io/metroidvania) pack by Kenmi + +--- + +#### [BrickBreaker](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Samples/tree/main/src/examples/Games/BrickBreaker) + +Breakout-style game with particles and advanced audio. + +**Features demonstrated:** + +- Physics-based ball movement +- Particle effects +- Advanced audio (music + sound effects) +- Collision detection +- Power-ups +- Score tracking + +**Complexity:** Intermediate +**Recommended for:** Learning physics and particle systems + +--- + +#### [Pong](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Samples/tree/main/src/examples/Games/Pong) + +Classic Pong with physics and collisions. + +**Features demonstrated:** + +- Simple physics +- AI opponent +- Collision detection +- Score system +- Basic audio + +**Complexity:** Beginner +**Recommended for:** First complete game project + +--- + +#### [Snake](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Samples/tree/main/src/examples/Games/Snake) + +Grid-based Snake game with entity pooling. + +**Features demonstrated:** + +- Grid-based movement +- Entity pooling +- Collision detection +- Score tracking +- Game state management + +**Complexity:** Beginner +**Recommended for:** Learning grid-based games + +--- + +#### [TicTacToe](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Samples/tree/main/src/examples/Games/TicTacToe) + +Turn-based game with simple AI. + +**Features demonstrated:** + +- Turn-based gameplay +- Simple AI +- UI elements +- Input handling +- Win condition detection + +**Complexity:** Beginner +**Recommended for:** Learning UI and game logic + +--- + +### Technical Demos + +#### [CameraDemo](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Samples/tree/main/src/examples/Demos/CameraDemo) + +Platformer demonstrating camera system and parallax scrolling. + +**Features demonstrated:** + +- Camera2D usage +- Following targets +- Parallax scrolling +- Viewport management +- Multi-layer backgrounds + +**Complexity:** Intermediate +**Recommended for:** Learning camera systems + +--- + +#### [SpritesDemo](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Samples/tree/main/src/examples/Demos/SpritesDemo) + +Showcase of different sprite formats. + +**Features demonstrated:** + +- 1bpp sprites +- 2bpp sprites +- 4bpp sprites +- Multi-layer sprites +- Sprite animation +- Horizontal flipping + +**Complexity:** Beginner +**Recommended for:** Understanding sprite formats + +**Note:** Requires `PIXELROOT32_ENABLE_2BPP_SPRITES=1` and `PIXELROOT32_ENABLE_4BPP_SPRITES=1` for full demo + +--- + +#### [TileMapDemo](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Samples/tree/main/src/examples/Demos/TileMapDemo) + +Demonstration of 4bpp tilemap rendering with viewport culling. + +**Features demonstrated:** + +- 4bpp tilemaps +- Viewport culling +- Palette caching +- Multi-layer tilemaps +- Performance optimization + +**Complexity:** Intermediate +**Recommended for:** Learning tilemap systems + +**Note:** Requires `PIXELROOT32_ENABLE_4BPP_SPRITES=1` + +--- + +## Learning Path + +### Beginner Path + +1. **[Pong](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Samples/tree/main/src/examples/Games/Pong)** - Start here for basic game structure +2. **[Snake](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Samples/tree/main/src/examples/Games/Snake)** - Learn grid-based games +3. **[TicTacToe](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Samples/tree/main/src/examples/Games/TicTacToe)** - Understand UI and turn-based logic +4. **[SpritesDemo](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Samples/tree/main/src/examples/Demos/SpritesDemo)** - Explore sprite formats + +### Intermediate Path + +1. **[Space Invaders](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Samples/tree/main/src/examples/Games/SpaceInvaders)** - Complete game with audio +2. **[BrickBreaker](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Samples/tree/main/src/examples/Games/BrickBreaker)** - Physics and particles +3. **[CameraDemo](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Samples/tree/main/src/examples/Demos/CameraDemo)** - Camera systems +4. **[TileMapDemo](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Samples/tree/main/src/examples/Demos/TileMapDemo)** - Tilemap rendering + +### Advanced Path + +1. **[Metroidvania](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Samples/tree/main/src/examples/Games/Metroidvania)** - Complex platformer +2. Study optimization techniques +3. Implement custom features +4. Create your own game + +--- + +## Example Structure + +Each example follows a consistent structure: + +``` +ExampleName/ +├── ExampleScene.h # Scene header +├── ExampleScene.cpp # Scene implementation +├── actors/ # Game-specific actors +│ ├── PlayerActor.h +│ ├── PlayerActor.cpp +│ └── ... +├── assets/ # Sprites, tilemaps, audio +│ ├── sprites/ +│ ├── tilemaps/ +│ └── audio/ +└── README.md # Example-specific documentation +``` + +--- + +## Running Examples + +### On ESP32 + +1. Open the project in PlatformIO +2. Select your target environment (e.g., `esp32dev`) +3. Build and upload +4. Monitor serial output for debugging + +### On Native (PC) + +1. Select the `native` environment +2. Build and run +3. Use SDL2 window for testing +4. Full debugging available + +--- + +## Common Patterns + +### Object Pooling + +Used in Space Invaders for projectiles: + +```cpp +class ProjectilePool { + std::vector pool; + +public: + Projectile* acquire() { + for (auto* p : pool) { + if (!p->isActive()) { + p->reset(); + return p; + } + } + return nullptr; // Pool exhausted + } +}; +``` + +### Collision Layers + +Used in most games for selective collision: + +```cpp +namespace Layers { + constexpr uint16_t PLAYER = 0x0001; + constexpr uint16_t ENEMY = 0x0002; + constexpr uint16_t PROJECTILE = 0x0004; +} + +player->setCollisionLayer(Layers::PLAYER); +player->setCollisionMask(Layers::ENEMY | Layers::PROJECTILE); +``` + +### State Management + +Used in all games for game flow: + +```cpp +enum class GameState { + MENU, + PLAYING, + PAUSED, + GAME_OVER +}; + +void update(unsigned long deltaTime) { + switch (currentState) { + case GameState::PLAYING: + updateGameplay(deltaTime); + break; + case GameState::PAUSED: + updatePauseMenu(deltaTime); + break; + // ... + } +} +``` + +### Tile-Based Collision + +Used in Metroidvania for efficient collision: + +```cpp +bool checkTileCollision(int tileX, int tileY) { + uint8_t tileIndex = tilemap.getTileAt(tileX, tileY); + return isSolidTile(tileIndex); +} + +void checkCollisions() { + int leftTile = (x - width/2) / TILE_SIZE; + int rightTile = (x + width/2) / TILE_SIZE; + int topTile = (y - height/2) / TILE_SIZE; + int bottomTile = (y + height/2) / TILE_SIZE; + + // Check surrounding tiles + if (checkTileCollision(leftTile, topTile)) { + // Handle collision + } +} +``` + +--- + +## Detailed Analysis + +For in-depth analysis of each example, including architecture, patterns, and lessons learned, see: + +- [Game Examples Guide](../reference/game_examples_guide.md) - Comprehensive analysis +- [Code Examples](../reference/code_examples.md) - Specific code patterns + +--- + +## Contributing Examples + +Want to contribute your own example? + +1. Follow the standard example structure +2. Include comprehensive comments +3. Add a README.md with description and features +4. Test on both ESP32 and Native platforms +5. Submit a pull request + +--- + +## Additional Resources + +- **Manual** - [Complete Manual](../manual/index.md) +- **API Reference** - [API Documentation](../api_reference/index.md) +- **Getting Started** - [Getting Started Guide](../getting_started/index.md) +- **Troubleshooting** - [Common Issues](../resources/troubleshooting.md) + +--- + +Ready to explore? Start with [Pong](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Samples/tree/main/src/examples/Games/Pong) for a simple introduction, or dive into [Space Invaders](https://github.com/PixelRoot32-Game-Engine/PixelRoot32-Game-Samples/tree/main/src/examples/Games/SpaceInvaders) for a complete game experience! diff --git a/docs/getting_started/fundamental_concepts.md b/docs/getting_started/fundamental_concepts.md index f5863f7..8b6ba9c 100644 --- a/docs/getting_started/fundamental_concepts.md +++ b/docs/getting_started/fundamental_concepts.md @@ -10,26 +10,58 @@ The **Engine** is the main class that orchestrates the entire system. Think of i - **Renderer**: Handles drawing everything on screen - **InputManager**: Reads and processes user input (buttons, keyboard) -- **AudioEngine**: Generates and plays sounds and music. In PixelRoot32, this is a **decoupled subsystem** that automatically adapts to your hardware, running on its own core (Dual-Core ESP32) or high-priority thread (PC/Single-Core) for maximum stability. +- **AudioEngine**: Generates and plays sounds and music (optional, enabled with `PIXELROOT32_ENABLE_AUDIO=1`) +- **MusicPlayer**: Plays background music tracks (optional, enabled with `PIXELROOT32_ENABLE_AUDIO=1`) - **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. +### Modular Compilation + +PixelRoot32 uses a **modular compilation system** that allows you to disable entire subsystems at compile time to save memory and reduce firmware size. This is especially useful on resource-constrained platforms like ESP32: + +- **Audio System** (`PIXELROOT32_ENABLE_AUDIO=0`) - Saves ~8KB RAM, 15-25% firmware size +- **Physics System** (`PIXELROOT32_ENABLE_PHYSICS=0`) - Saves ~12KB RAM, 20-30% firmware size +- **UI System** (`PIXELROOT32_ENABLE_UI_SYSTEM=0`) - Saves ~4KB RAM, 8-15% firmware size +- **Particle System** (`PIXELROOT32_ENABLE_PARTICLES=0`) - Saves ~6KB RAM, 10-20% firmware size + +You can disable these systems in your `platformio.ini` file: + +```ini +build_flags = + -D PIXELROOT32_ENABLE_AUDIO=0 # Disable audio + -D PIXELROOT32_ENABLE_PHYSICS=0 # Disable physics +``` + ### 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 +- **Managing collisions** between entities that can collide (if physics is enabled) + +### Scene Stack and SceneManager + +The **SceneManager** manages a stack of scenes, allowing you to: + +- **setCurrentScene()**: Replace the current scene with a new one +- **pushScene()**: Push a new scene onto the stack (pauses the previous one) +- **popScene()**: Remove the top scene and resume the previous one + +This is useful for implementing pause menus, dialogs, or overlays without losing the state of the underlying scene. + +### Scene Memory Management -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). +Scenes follow a **non-owning** model for entities. When you call `addEntity(Entity*)`, the scene stores a reference but does not take ownership. You are responsible for the entity's lifetime, typically using `std::unique_ptr` in your Scene subclass. ### Entity: The Fundamental Building Blocks @@ -48,41 +80,55 @@ Each entity has two main methods: ### 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: +An **Actor** is a special entity that can participate in the collision system (available when `PIXELROOT32_ENABLE_PHYSICS=1`). 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) +- **Collision shape**: Can be AABB (rectangle) or CIRCLE +- **Hitbox**: The shape used to detect collisions 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.). +When two actors collide, the system calls their **onCollision()** method so they can react (e.g., player loses health, enemy is destroyed, etc.). Note that collision responses (velocity/position changes) are handled automatically by the CollisionSystem. ### PhysicsActor: Entities with Physics -A **PhysicsActor** is an Actor that also has physical properties: +A **PhysicsActor** is an Actor that also has physical properties (available when `PIXELROOT32_ENABLE_PHYSICS=1`): + - **Velocity** (vx, vy): Moves automatically according to its velocity -- **Gravity**: Can fall automatically +- **Mass**: Affects collision response +- **Gravity**: Can fall automatically (controlled by gravityScale) - **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 +### Specialized Actor Types -The relationship between these classes is hierarchical: +Following the Godot Engine philosophy, PixelRoot32 provides specialized actor types for different use cases: ``` Entity (base) └── Actor (can collide) - └── PhysicsActor (has physics) + └── PhysicsActor (base physics) + ├── StaticActor (immovable walls/floors) + ├── KinematicActor (character movement, move_and_slide) + └── RigidActor (props, physical objects with gravity) ``` +- **StaticActor**: Immovable bodies like walls and floors. Optimized to skip the spatial grid. +- **KinematicActor**: For logic-driven movement (players, moving platforms). Use `moveAndCollide()` or `moveAndSlide()` for manual control with collision detection. +- **RigidActor**: Fully automatic physics simulation. Affected by gravity, forces, and collisions. Ideal for debris, boxes, and physical props. + 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 +- You can use StaticActor for immovable obstacles +- You can use KinematicActor for player-controlled characters +- You can use RigidActor for objects that need automatic physics ## Rendering System @@ -98,6 +144,12 @@ Layers are drawn in order: first 0, then 1, and finally 2. This ensures the back 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. +The default configuration supports 3 layers (`MAX_LAYERS=3`), which is optimized for ESP32 memory constraints. You can override this in your `platformio.ini` if needed: + +```ini +build_flags = -DMAX_LAYERS=5 +``` + ### Resolution Scaling PixelRoot32 supports **Independent Resolution Scaling**. This means your game logic can run at a different resolution (the **logical resolution**) than the physical screen (**physical resolution**). @@ -115,7 +167,19 @@ The rendering process works like this: 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). +The Renderer abstracts hardware details through the DrawSurface interface, so the same code works on both ESP32 (TFT_eSPI, U8G2) and PC (SDL2). + +### Color Palettes + +PixelRoot32 uses indexed color palettes for efficient rendering. You can choose from several built-in palettes: + +- **PR32**: The standard PixelRoot32 palette (default) +- **NES**: Nintendo Entertainment System inspired +- **GB**: Game Boy inspired (4 greens) +- **GBC**: Game Boy Color inspired +- **PICO8**: PICO-8 fantasy console palette + +The engine also supports **dual palette mode**, allowing separate palettes for backgrounds and sprites, and **multi-palette tilemaps** (2bpp/4bpp): each tilemap cell can use one of up to 8 background palette slots for different colours per layer or region. ### Coordinates and Space @@ -133,29 +197,79 @@ Coordinates are in pixels. If your display is 240x240, coordinates range from (0 When your game starts: 1. **Configuration**: Configuration objects are created (DisplayConfig, InputConfig, AudioConfig) -2. **Engine**: The Engine is created with these configurations +2. **Engine**: The Engine is created with these configurations (using move semantics for DisplayConfig) 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 +5. **setScene()**: The scene is assigned to the Engine using `engine.setScene()` + +Example: + +```cpp +// Create configuration +DisplayConfig displayConfig = /* ... */; +InputConfig inputConfig(3, 12, 14, 27); // 3 buttons on pins 12, 14, 27 + +// Create engine (DisplayConfig is moved) +Engine engine(std::move(displayConfig), inputConfig); +engine.init(); + +// Create and set initial scene +MyScene* scene = new MyScene(); +scene->init(); +engine.setScene(scene); + +// Start game loop +engine.run(); +``` + +## Physics System (Optional) + +When the physics system is enabled (`PIXELROOT32_ENABLE_PHYSICS=1`), PixelRoot32 uses a **"Flat Solver"** architecture optimized for microcontrollers: + +### Collision Detection + +The system uses a two-phase approach: + +1. **Broadphase**: Uniform Spatial Grid with O(1) cell hashing to quickly find potential collisions +2. **Narrowphase**: Precise collision detection for AABB vs AABB, Circle vs Circle, and Circle vs AABB + +### Physics Pipeline + +Each frame, the CollisionSystem executes in strict order: + +1. **Detect Collisions**: Find all overlapping bodies +2. **Solve Velocity**: Apply impulse-based collision response (2 iterations by default) +3. **Integrate Positions**: Update positions based on velocity (`p = p + v * dt`) +4. **Solve Penetration**: Correct overlaps using Baumgarte stabilization +5. **Trigger Callbacks**: Call `onCollision()` for gameplay notifications + +### Key Features + +- **Fixed Timestep**: Deterministic 1/60s simulation +- **Continuous Collision Detection (CCD)**: For fast-moving circular objects +- **Memory Optimized**: Shared static buffers save ~100KB DRAM +- **Collision Layers**: Up to 16 layers for flexible collision filtering + +This architecture provides stable, predictable physics while remaining efficient enough for ESP32 microcontrollers. ### Game Loop Once initialized, the Engine enters the **game loop**: -``` +```text While the game is running: 1. Calculate deltaTime (time since last frame) 2. Update InputManager (read buttons/keyboard) 3. Update current scene (update all entities) - 4. Detect collisions in the scene + 4. Detect collisions in the scene (if physics enabled) 5. Draw the scene (draw all visible entities) 6. Repeat - -*Note: The audio system runs independently on a separate core/thread, ensuring sample-accurate music and SFX even if the game loop slows down.* ``` This cycle runs continuously, typically at 30-60 FPS on ESP32, or faster on PC. +**Note**: The audio system (when enabled) runs independently on a separate core/thread on ESP32, ensuring sample-accurate music and SFX even if the game loop slows down. + ### Update Each frame, all enabled entities receive a call to their `update(deltaTime)` method. This is where: @@ -179,24 +293,42 @@ 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 +Remember that scenes don't own entities, so you're responsible for managing their lifetime (typically using `std::unique_ptr` in your Scene class). + ## 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 +2. **SceneManager** manages a stack of scenes (push/pop for menus and overlays) +3. **Scene** organizes your game into screens/levels +4. **Entity** is any object in your game +5. **Actor** is an entity that can collide (optional, requires physics system) +6. **PhysicsActor** is the base for physics-enabled actors +7. **StaticActor** is for immovable obstacles +8. **KinematicActor** is for player-controlled movement +9. **RigidActor** is for objects with automatic physics +10. **Renderer** draws everything on screen using layers +11. 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. +### Optional Subsystems + +Remember that several subsystems are optional and can be disabled to save memory: + +- **Audio System** (`PIXELROOT32_ENABLE_AUDIO=0`) - Saves ~8KB RAM +- **Physics System** (`PIXELROOT32_ENABLE_PHYSICS=0`) - Saves ~12KB RAM +- **UI System** (`PIXELROOT32_ENABLE_UI_SYSTEM=0`) - Saves ~4KB RAM +- **Particle System** (`PIXELROOT32_ENABLE_PARTICLES=0`) - Saves ~6KB RAM + +This modular design allows you to create games that fit within the constraints of your target hardware. + ## Next Step Now that you understand the fundamental concepts, you're ready to [create your first project](your_first_project.md) and see these concepts in action with real code. diff --git a/docs/getting_started/index.md b/docs/getting_started/index.md new file mode 100644 index 0000000..bb8ff24 --- /dev/null +++ b/docs/getting_started/index.md @@ -0,0 +1,173 @@ +# Getting Started with PixelRoot32 + +Welcome to PixelRoot32! This guide will help you get started with the engine, from understanding what it is to creating your first game. + +## What You'll Learn + +This section covers everything you need to know to start developing games with PixelRoot32: + +1. **Understanding the Engine** - Learn what PixelRoot32 is and why it's perfect for ESP32 game development +2. **Installation** - Set up your development environment +3. **Core Concepts** - Understand the fundamental architecture +4. **First Project** - Create and run your first game + +## Quick Start Path + +If you're eager to start coding, follow this path: + +1. [What is PixelRoot32?](what_is_pixelroot32.md) - 5 min read +2. [Installation](installation.md) - 15-30 min setup +3. [Your First Project](your_first_project.md) - 30 min tutorial +4. [Fundamental Concepts](fundamental_concepts.md) - 20 min read + +## Sections + +### [What is PixelRoot32?](what_is_pixelroot32.md) + +Learn about the PixelRoot32 Game Engine, its features, and what makes it unique for ESP32 development. + +**Topics covered:** +- Engine overview +- Key features +- Target platforms +- Use cases + +**Time to read:** ~5 minutes + +--- + +### [Why PixelRoot32?](why_pixelroot32.md) + +Understand the design philosophy and advantages of using PixelRoot32 for your game projects. + +**Topics covered:** +- Design philosophy +- Advantages over alternatives +- When to use PixelRoot32 +- Success stories + +**Time to read:** ~5 minutes + +--- + +### [Installation](installation.md) + +Step-by-step guide to setting up your development environment for PixelRoot32. + +**Topics covered:** +- Prerequisites +- PlatformIO setup +- ESP32 configuration +- Native (PC) setup +- Troubleshooting + +**Time to complete:** ~15-30 minutes + +--- + +### [Fundamental Concepts](fundamental_concepts.md) + +Deep dive into the core concepts and architecture of PixelRoot32. + +**Topics covered:** +- Scene-based architecture +- Entity system +- Rendering pipeline +- Input management +- Audio system +- Physics system +- Modular compilation + +**Time to read:** ~20 minutes + +--- + +### [Your First Project](your_first_project.md) + +Hands-on tutorial to create your first game with PixelRoot32. + +**Topics covered:** +- Project setup +- Creating a scene +- Adding entities +- Handling input +- Drawing graphics +- Running on hardware + +**Time to complete:** ~30 minutes + +--- + +## Prerequisites + +Before starting, you should have: + +- **Basic C++ knowledge** - Understanding of classes, pointers, and basic syntax +- **Arduino/ESP32 familiarity** - Basic understanding of microcontroller programming (helpful but not required) +- **Development hardware** - ESP32 board (ESP32, ESP32-S3, ESP32-C3, etc.) or PC for native development + +## Development Platforms + +PixelRoot32 supports two development platforms: + +### ESP32 (Primary Target) +- **ESP32 Classic** - Full features including DAC audio +- **ESP32-S3** - Enhanced performance, I2S audio only +- **ESP32-C3** - Budget-friendly, automatic Fixed16 math +- **ESP32-S2** - Single-core with USB OTG +- **ESP32-C6** - Latest with WiFi 6 + +See [Platform Compatibility](../manual/optimization/platform_compatibility.md) for detailed comparison. + +### Native (PC/Mac/Linux) +- **SDL2-based** - Cross-platform development +- **Rapid testing** - No hardware upload needed +- **Full debugging** - Use your favorite IDE debugger + +## Learning Path + +### Beginner Path +1. Read [What is PixelRoot32?](what_is_pixelroot32.md) +2. Complete [Installation](installation.md) +3. Follow [Your First Project](your_first_project.md) +4. Explore [Code Examples](../reference/code_examples.md) + +### Intermediate Path +1. Study [Fundamental Concepts](fundamental_concepts.md) +2. Read [Engine Architecture](../manual/engine_architecture.md) +3. Learn about [Scenes and Entities](../manual/game_development/scenes_and_entities.md) +4. Explore [Game Examples](../reference/game_examples_guide.md) + +### Advanced Path +1. Deep dive into [API Reference](../api_reference/index.md) +2. Study [Memory Management](../manual/optimization/memory_management.md) +3. Learn [Performance Tuning](../manual/optimization/performance_tuning.md) +4. Explore [Extensibility](../manual/optimization/extensibility.md) + +## Next Steps + +After completing the Getting Started guide: + +- **Explore the Manual** - Detailed guides on specific topics +- **Check API Reference** - Complete API documentation +- **Try Examples** - Learn from working game examples +- **Join Community** - Get help and share your projects + +## Need Help? + +- **FAQ** - [Frequently Asked Questions](../resources/faq.md) +- **Troubleshooting** - [Common Issues and Solutions](../resources/troubleshooting.md) +- **Discord** - Join our community for real-time help +- **GitHub** - Report issues and contribute + +## Quick Links + +- [Installation Guide](installation.md) +- [Your First Project](your_first_project.md) +- [API Reference](../api_reference/index.md) +- [Manual](../manual/index.md) +- [Examples](../reference/game_examples_guide.md) + +--- + +Ready to start? Head to [What is PixelRoot32?](what_is_pixelroot32.md) to begin your journey! diff --git a/docs/getting_started/your_first_project.md b/docs/getting_started/your_first_project.md index c8aec6f..97e7fc9 100644 --- a/docs/getting_started/your_first_project.md +++ b/docs/getting_started/your_first_project.md @@ -16,11 +16,13 @@ This guide will walk you through creating and running your first PixelRoot32 pro ### For ESP32 Development -- **ESP32 Board**: Any ESP32 development board (ESP32-WROOM, ESP32-WROVER, etc.) +- **ESP32 Board**: Any ESP32 variant (ESP32, ESP32-S3, ESP32-C3, 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) +- **TFT Display**: Compatible display (ST7789, ST7735, etc.) +- **Buttons**: Optional for first project (can use dummy pins) +- **Audio Hardware** (optional): + - ESP32 Classic: Internal DAC (GPIO 25/26) or I2S amplifier + - ESP32-S3/C3/S2: I2S amplifier required (no internal DAC) ### For Native (PC) Development @@ -70,14 +72,12 @@ This guide will walk you through creating and running your first PixelRoot32 pro board = esp32dev framework = arduino lib_deps = - gperez88/PixelRoot32-Game-Engine@1.0.0 + gperez88/PixelRoot32-Game-Engine@^1.0.0 ``` - > **⚠️ IMPORTANT**: Use the exact version `1.0.0` for stable release. Do NOT use `^` or fuzzy versioning for production builds. - 3. Save the file. PlatformIO will automatically download the library. -### Option B: Git Submodule +### Option B: Git Submodule (For Development) 1. Open terminal in your project root @@ -87,30 +87,69 @@ This guide will walk you through creating and running your first PixelRoot32 pro git submodule add https://github.com/Gperez88/PixelRoot32-Game-Engine.git lib/PixelRoot32-Game-Engine ``` -3. Update `platformio.ini`: - - ```ini - lib_extra_dirs = lib - ``` - -## Step 3: Configure Hardware (ESP32) +3. No additional configuration needed - PlatformIO will detect it automatically. -### Configure TFT_eSPI Display +## Step 3: Configure Hardware and Build Profiles (ESP32) -Edit `platformio.ini` and add build flags for your display. Here are two common configurations: +PixelRoot32 uses a modular compilation system that allows you to enable/disable subsystems to save memory. This is configured using build profiles in `platformio.ini`. -**For ST7789 (240x240):** +### Basic Configuration Structure ```ini -[env:esp32dev] +# Base ESP32 configuration +[base_esp32] platform = espressif32 -board = esp32dev framework = arduino lib_deps = - gperez88/PixelRoot32-Game-Engine@1.0.0 + gperez88/PixelRoot32-Game-Engine@^1.0.0 bodmer/TFT_eSPI@^2.5.43 +build_flags = + -std=gnu++17 + -fno-exceptions + -fno-rtti + +# Full-featured profile (all subsystems enabled) +[profile_full] +build_flags = + -D PIXELROOT32_ENABLE_AUDIO=1 + -D PIXELROOT32_ENABLE_PHYSICS=1 + -D PIXELROOT32_ENABLE_UI_SYSTEM=1 + -D PIXELROOT32_ENABLE_PARTICLES=1 + +# Minimal profile (only core rendering) +[profile_minimal] +build_flags = + -D PIXELROOT32_ENABLE_AUDIO=0 + -D PIXELROOT32_ENABLE_PHYSICS=0 + -D PIXELROOT32_ENABLE_UI_SYSTEM=1 + -D PIXELROOT32_ENABLE_PARTICLES=0 + +# Final environment combining base + profile +[env:esp32_full] +extends = base_esp32, profile_full +board = esp32dev +build_flags = + ${base_esp32.build_flags} + ${profile_full.build_flags} + # Display configuration + -D ST7789_DRIVER + -D TFT_WIDTH=240 + -D TFT_HEIGHT=240 + -D TFT_MOSI=23 + -D TFT_SCLK=18 + -D TFT_DC=2 + -D TFT_RST=4 + -D TFT_CS=-1 + -D SPI_FREQUENCY=40000000 +``` + +### Display Configuration Examples +**For ST7789 (240x240):** + +```ini build_flags = + # ... other flags ... -D ST7789_DRIVER -D TFT_WIDTH=240 -D TFT_HEIGHT=240 @@ -119,22 +158,14 @@ build_flags = -D TFT_DC=2 -D TFT_RST=4 -D TFT_CS=-1 - -D LOAD_GLCD - -D LOAD_FONT2 - -D LOAD_FONT4 - -D LOAD_FONT6 - -D LOAD_FONT7 - -D LOAD_FONT8 - -D LOAD_GFXFF - -D SMOOTH_FONT -D SPI_FREQUENCY=40000000 - -D SPI_READ_FREQUENCY=20000000 ``` **For ST7735 (128x128):** ```ini build_flags = + # ... other flags ... -D ST7735_DRIVER -D ST7735_GREENTAB3 -D TFT_WIDTH=128 @@ -144,27 +175,20 @@ build_flags = -D TFT_DC=2 -D TFT_RST=4 -D TFT_CS=-1 - -D LOAD_GLCD - -D LOAD_FONT2 - -D LOAD_FONT4 - -D LOAD_FONT6 - -D LOAD_FONT7 - -D LOAD_FONT8 - -D LOAD_GFXFF - -D SMOOTH_FONT -D SPI_FREQUENCY=27000000 - -D SPI_READ_FREQUENCY=20000000 ``` -> **Note**: Adjust the pin numbers (`TFT_MOSI`, `TFT_SCLK`, `TFT_DC`, `TFT_RST`) to match your hardware wiring. +> **Note**: Adjust pin numbers to match your hardware wiring. -### Configure Input (Optional for First Project) +### Memory Savings by Profile -If you have buttons connected, note the GPIO pins. For now, we'll create a project that works without input. +| Profile | Audio | Physics | UI | Particles | RAM Saved | Firmware Reduction | +|---------|-------|---------|----|-----------|-----------|--------------------| +| Full | ✅ | ✅ | ✅ | ✅ | 0KB | 0% | +| Arcade | ✅ | ✅ | ❌ | ✅ | ~4KB | 8-15% | +| Minimal | ❌ | ❌ | ✅ | ❌ | ~26KB | 50-70% | -### Configure Audio (Optional for First Project) - -Audio is optional for the first project. We'll add it later. +For detailed platform compatibility, see the [Platform Compatibility Guide](../manual/optimization/platform_compatibility.md). ## Step 4: Create Your First Scene @@ -176,30 +200,29 @@ Create a new file `src/MyFirstScene.h`: #include #include -class MyFirstScene : public pixelroot32::core::Scene { +using namespace pixelroot32; + +class MyFirstScene : public core::Scene { public: void init() override { // Called when the scene is initialized - // Set up your scene here } void update(unsigned long deltaTime) override { - // Called every frame - // Update game logic here - Scene::update(deltaTime); // Don't forget to call parent update! + // Called every frame - update game logic here + Scene::update(deltaTime); } - void draw(pixelroot32::graphics::Renderer& renderer) override { - // Called every frame to draw - // Draw your scene here - - // Example: Draw a simple rectangle - renderer.drawFilledRectangle(50, 50, 100, 100, pixelroot32::graphics::Color::Blue); + void draw(graphics::Renderer& renderer) override { + // Draw a blue rectangle + renderer.drawFilledRectangle(50, 50, 100, 100, + graphics::Color::resolveColor(graphics::Color::Blue)); - // Example: Draw text - renderer.drawText("Hello PixelRoot32!", 20, 20, pixelroot32::graphics::Color::White, 2); + // Draw text using the default font + renderer.drawText("Hello PixelRoot32!", 20, 20, + graphics::Color::White, 2); - // Don't forget to call parent draw to draw all entities! + // Draw all entities in the scene Scene::draw(renderer); } }; @@ -212,42 +235,28 @@ Replace the contents of `src/main.cpp` with: ```cpp #include #include -#include -#include +#include +#include #include "MyFirstScene.h" -namespace pr32 = pixelroot32; +using namespace pixelroot32; -// Audio configuration (optional - can be omitted for first project) -const int DAC_PIN = 25; // GPIO 25 or 26 -pr32::drivers::esp32::ESP32_DAC_AudioBackend audioBackend(DAC_PIN, 11025); - -// Display configuration -// 128x128 game logic scaled to 240x240 display -pr32::graphics::DisplayConfig displayConfig( - pr32::graphics::DisplayType::ST7789, - 0, // rotation +// Display configuration: 128x128 logical resolution scaled to 240x240 physical +graphics::DisplayConfig displayConfig( + graphics::DisplayType::ST7789, + 0, // rotation 240, 240, // physical resolution (hardware) - 128, 128 // logical resolution (rendering space) + 128, 128 // logical resolution (game rendering) ); -// Input configuration (6 buttons: UP, DOWN, LEFT, RIGHT, A, B) -// For now, we'll use dummy pins - you can change these later -pr32::input::InputConfig inputConfig( +// Input configuration (dummy pins for first project) +input::InputConfig inputConfig( 6, // button count - 32, // UP pin - 27, // DOWN pin - 33, // LEFT pin - 14, // RIGHT pin - 13, // A button pin - 12 // B button pin + 32, 27, 33, 14, 13, 12 // GPIO pins (adjust as needed) ); -// Audio configuration -pr32::audio::AudioConfig audioConfig(&audioBackend, audioBackend.getSampleRate()); - -// Create the engine -pr32::core::Engine engine(displayConfig, inputConfig, audioConfig); +// Create the engine (move semantics for DisplayConfig) +core::Engine engine(std::move(displayConfig), inputConfig); // Create your scene MyFirstScene myScene; @@ -271,6 +280,67 @@ void loop() { } ``` +### With Audio (Optional) + +If you want audio support, add the audio backend: + +```cpp +#include +#include +#include +#include + +// Only include if audio is enabled +#if PIXELROOT32_ENABLE_AUDIO +#include