Scene¶
Represents a game level or screen containing entities.
Description¶
A Scene manages a collection of Entities and a CollisionSystem. It is responsible for updating and drawing all entities it contains. Scenes provide lifecycle hooks (init(), update(), draw()) to manage gameplay segments.
Scenes are the primary organizational unit in PixelRoot32, similar to levels or screens in other game engines. Each scene can contain up to MAX_ENTITIES (default 32; defined in platforms/EngineConfig.h) entities, and drawing uses up to MAX_LAYERS (default 3; defined in platforms/EngineConfig.h) render layers.
Namespace¶
Inheritance¶
- Base class: None (abstract base class)
- Inherited by: Your custom scene classes (e.g.,
MainMenuScene,GameScene)
Constructors¶
Scene()¶
Creates an empty scene ready to be populated with entities.
Notes: - The scene starts with no entities - init() should be called when the scene becomes active - The collision system is automatically initialized
Example:
#include <memory> // Required for std::unique_ptr
class MyScene : public pixelroot32::core::Scene {
public:
void init() override {
// Initialize scene resources
}
void update(unsigned long deltaTime) override {
Scene::update(deltaTime); // Update entities and collisions
}
void draw(pixelroot32::graphics::Renderer& renderer) override {
Scene::draw(renderer); // Draw all entities
}
};
Public Methods¶
virtual void init()¶
Initializes the scene. Called when entering the scene.
Returns: - void
Notes: - Called automatically when the scene is set via Engine::setScene() - Override this method to initialize scene-specific resources - Safe to call multiple times (idempotent) - Add entities here or in the constructor
Example:
#include <memory>
#include <vector>
class GameScene : public pixelroot32::core::Scene {
private:
std::unique_ptr<PlayerActor> player;
std::vector<std::unique_ptr<EnemyActor>> enemies;
public:
void init() override {
// Create player
player = std::make_unique<PlayerActor>();
addEntity(player.get());
// Create enemies
for (int i = 0; i < 10; i++) {
auto enemy = std::make_unique<EnemyActor>();
addEntity(enemy.get());
enemies.push_back(std::move(enemy));
}
}
};
virtual void update(unsigned long deltaTime)¶
Updates all entities in the scene and handles collisions.
Parameters: - deltaTime (unsigned long): Time elapsed since last frame in milliseconds
Notes: - Called automatically by the engine every frame - Updates all entities in the scene - Processes collisions between actors - Override to add custom update logic, but call Scene::update(deltaTime) to maintain entity updates
Example:
void update(unsigned long deltaTime) override {
// Custom update logic
gameTimer += deltaTime;
// Update entities and collisions
Scene::update(deltaTime);
// Additional logic after entity updates
if (gameTimer > 60000) {
// Game over after 60 seconds
}
}
virtual void draw(Renderer& renderer)¶
Draws all visible entities in the scene.
Parameters: - renderer (pixelroot32::graphics::Renderer&): The renderer to use for drawing
Notes: - Called automatically by the engine every frame after update() - Draws all visible entities in the scene - Override to add custom drawing logic, but call Scene::draw(renderer) to maintain entity rendering - Entities are drawn in the order they were added
Example:
void draw(pixelroot32::graphics::Renderer& renderer) override {
// Draw background
renderer.drawTileMap(backgroundTileMap, 0, 0, Color::White);
// Draw all entities
Scene::draw(renderer);
// Draw UI overlay
renderer.drawText("Score: 100", 10, 10, Color::White, 1);
}
void addEntity(Entity* entity)¶
Adds an entity to the scene.
Parameters: - entity (Entity*): Pointer to the Entity to add. Must not be nullptr.
Notes: - Entities are added to an internal queue - Maximum of MAX_ENTITIES (default 32; overridable) entities per scene - If the limit is reached, the entity may not be added (check return value if available) - Entities are updated and drawn in the order they were added - Important: The scene does NOT take ownership of the entity. You must ensure the entity remains valid while it is in the scene (e.g., by storing it in a std::unique_ptr member).
Example:
void init() override {
// Create and add player
// Note: Store the unique_ptr in the class member to manage its lifetime.
auto playerPtr = std::make_unique<PlayerActor>();
playerPtr->setPosition(64, 64);
addEntity(playerPtr.get());
// Transfer ownership to a member variable
this->player = std::move(playerPtr);
// Create and add enemy
auto enemyPtr = std::make_unique<EnemyActor>();
enemyPtr->setPosition(100, 100);
addEntity(enemyPtr.get());
// Transfer ownership to a container
this->enemies.push_back(std::move(enemyPtr));
}
void removeEntity(Entity* entity)¶
Removes an entity from the scene.
Parameters: - entity (Entity*): Pointer to the Entity to remove
Notes: - The entity is removed from the update and draw queues - The entity is not deleted automatically (you must manage its lifetime) - Safe to call even if the entity is not in the scene - Consider using object pooling instead of frequent add/remove
Example:
void onEnemyDestroyed(EnemyActor* enemy) {
removeEntity(enemy);
// Return to pool or delete
enemyPool.returnToPool(enemy);
}
void clearEntities()¶
Removes all entities from the scene.
Notes: - All entities are removed from the update and draw queues - Entities are not deleted automatically (you must manage their lifetimes) - Useful for scene cleanup or reset - Consider using object pooling to reuse entities
Example:
void reset() {
clearEntities();
// Return all entities to pool
for (auto* entity : entityPool) {
entityPool.returnToPool(entity);
}
}
Protected Members¶
ArduinoQueue entities¶
Queue of entities in the scene. Accessible to derived classes for custom entity management.
Type: ArduinoQueue<Entity*>
Notes: - Maximum capacity: MAX_ENTITIES (default 32; overridable) - Direct access allows custom iteration or filtering - Use with caution: modifying while iterating may cause issues
CollisionSystem collisionSystem¶
System to handle collisions between actors. Accessible to derived classes for custom collision handling.
Type: pixelroot32::physics::CollisionSystem
Notes: - Automatically processes collisions between actors - Uses collision layers and masks for filtering - Can be accessed for custom collision queries
Overriding scene limits (MAX_LAYERS / MAX_ENTITIES)¶
The engine defines default limits in core/Scene.h: MAX_LAYERS (default 3) and MAX_ENTITIES (default 32). These are guarded with #ifndef, so you can override them from your project without modifying the engine.
ESP32 platform limitation
The default of 3 for MAX_LAYERS is due to ESP32 platform constraints (memory and draw-loop cost). On native/PC you can safely use a higher value; on ESP32, increasing it may affect performance or memory.
Option A: Compiler flags (recommended)¶
In your project (e.g. in platformio.ini), add the defines to build_flags for the environment you use:
The compiler defines MAX_LAYERS and MAX_ENTITIES before processing any .cpp file. Because Scene.h uses #ifndef MAX_LAYERS / #ifndef MAX_ENTITIES, it will not redefine them and your values will be used.
Effect: - MAX_LAYERS: Number of render layers drawn in Scene::draw() (layer 0 = background, 1+ = sprite context). Increasing this allows more distinct draw layers (e.g. background, platforms, gameplay, foreground, UI). - MAX_ENTITIES: On Arduino, the capacity of the scene entity queue when constructed with this value. On native (mock queue), the value is ignored (unbounded).
See also: Platforms and Drivers - Scene limits.
Usage Example¶
#include "core/Scene.h"
#include "core/Actor.h"
#include <memory>
#include <vector>
class MyGameScene : public pixelroot32::core::Scene {
private:
std::unique_ptr<PlayerActor> player;
std::vector<std::unique_ptr<EnemyActor>> enemies;
public:
void init() override {
// Create player
player = std::make_unique<PlayerActor>();
player->setPosition(64, 64);
addEntity(player.get());
// Create some enemies
for (int i = 0; i < 5; i++) {
auto enemy = std::make_unique<EnemyActor>();
enemy->setPosition(10 + i * 20, 10);
addEntity(enemy.get());
enemies.push_back(std::move(enemy));
}
}
void update(unsigned long deltaTime) override {
// Custom game logic
if (player->isDead()) {
// Handle game over
}
// Update entities and collisions
Scene::update(deltaTime);
}
void draw(pixelroot32::graphics::Renderer& renderer) override {
// Draw background
renderer.drawFilledRectangle(0, 0, 128, 128, Color::Black);
// Draw all entities
Scene::draw(renderer);
// Draw HUD
char scoreText[32];
snprintf(scoreText, sizeof(scoreText), "Score: %d", score);
renderer.drawText(scoreText, 10, 10, Color::White, 1);
}
};
Performance Considerations¶
- Entity limit:
MAX_ENTITIES(default 32) can be overridden via compiler flags; plan accordingly - Add/Remove: Frequent add/remove operations can be expensive; use object pooling
- Update order: Entities are updated in add order; consider order for dependencies
- Collision checks: CollisionSystem automatically handles actor collisions efficiently
ESP32 Considerations¶
- Memory: Each entity consumes memory; stay well below the limit
- Object pooling: Essential for ESP32 to avoid memory fragmentation
- Scene switching: Clearing and recreating scenes can fragment memory; reuse scenes when possible
See Also¶
- Entity - Base entity class
- Actor - Entity with collision support
- PhysicsActor - Entity with physics
- CollisionSystem - Collision detection
- Manual - Scenes and Entities
- API Overview