Skip to content

Scenes and Entities

Scenes and entities are the foundation of every PixelRoot32 game. This guide teaches you how to organize your game using scenes and create interactive game objects with entities.

Creating a Scene

A Scene represents a screen or level in your game. To create a scene, inherit from pixelroot32::core::Scene and implement the three main methods:

#include <core/Scene.h>
#include <graphics/Renderer.h>

class MyGameScene : public pixelroot32::core::Scene {
public:
    void init() override {
        // Called once when the scene is initialized
        // Set up your scene here: create entities, load resources, etc.
    }

    void update(unsigned long deltaTime) override {
        // Called every frame
        // Update game logic here

        // IMPORTANT: Always call parent update to update all entities
        Scene::update(deltaTime);
    }

    void draw(pixelroot32::graphics::Renderer& renderer) override {
        // Called every frame to draw
        // Draw your scene here

        // IMPORTANT: Always call parent draw to draw all entities
        Scene::draw(renderer);
    }
};

Scene Lifecycle

  1. init(): Called once when the scene is set as active
  2. Create and initialize entities
  3. Set up game state
  4. Load resources
  5. Configure palettes, audio, etc.

  6. update(deltaTime): Called every frame

  7. Process input
  8. Update game logic
  9. Handle collisions
  10. Must call Scene::update(deltaTime) to update all entities

  11. draw(renderer): Called every frame

  12. Draw background elements
  13. Draw UI elements
  14. Must call Scene::draw(renderer) to draw all entities

Basic Entities

An Entity is any object in your game. To create an entity, inherit from pixelroot32::core::Entity:

#include <core/Entity.h>
#include <graphics/Renderer.h>
#include <graphics/Color.h>

class SimpleEntity : public pixelroot32::core::Entity {
public:
    SimpleEntity(float x, float y)
        : Entity(x, y, 16, 16, pixelroot32::core::EntityType::GENERIC) {
        // Set render layer (0=background, 1=gameplay, 2=UI)
        setRenderLayer(1);
    }

    void update(unsigned long deltaTime) override {
        // Update entity logic
        // For example, move the entity
        this->x += 1.0f * (deltaTime * 0.001f); // Move right at 1 pixel per second
    }

    void draw(pixelroot32::graphics::Renderer& renderer) override {
        // Draw the entity
        renderer.drawFilledRectangle(
            static_cast<int>(x), 
            static_cast<int>(y), 
            width, 
            height, 
            pixelroot32::graphics::Color::Red
        );
    }
};

Entity Properties

Every entity has these properties:

  • x, y: Position in world space (float)
  • width, height: Dimensions (int)
  • isVisible: If false, draw() is not called
  • isEnabled: If false, update() is not called
  • renderLayer: Which layer to draw on (0, 1, or 2)

Adding Entities to a Scene

Add entities to your scene in init():

void MyGameScene::init() override {
    // Create entities
    SimpleEntity* entity1 = new SimpleEntity(50, 50);
    SimpleEntity* entity2 = new SimpleEntity(100, 100);

    // Add them to the scene
    addEntity(entity1);
    addEntity(entity2);
}

The scene automatically manages these entities: - Calls update() on all enabled entities each frame - Calls draw() on all visible entities each frame - Handles cleanup when the scene is destroyed

Actors: Entities with Collisions

An Actor is an entity that can participate in collision detection. Inherit from pixelroot32::core::Actor:

#include <core/Actor.h>
#include <physics/CollisionTypes.h>

class MyActor : public pixelroot32::core::Actor {
public:
    MyActor(float x, float y, int w, int h)
        : Actor(x, y, w, h) {
        // Set collision layer (what group this actor belongs to)
        setCollisionLayer(0x0001); // Example: layer 1

        // Set collision mask (what groups this actor can collide with)
        setCollisionMask(0x0002); // Example: can collide with layer 2
    }

    void update(unsigned long deltaTime) override {
        // Update actor logic
    }

    void draw(pixelroot32::graphics::Renderer& renderer) override {
        // Draw the actor
    }

    // REQUIRED: Define the hitbox for collision detection
    pixelroot32::core::Rect getHitBox() override {
        return {x, y, width, height};
    }

    // REQUIRED: Handle collisions
    void onCollision(pixelroot32::core::Actor* other) override {
        // React to collision
        // For example: take damage, destroy self, etc.
    }
};

Collision Layers and Masks

Collision layers use bit flags to organize actors into groups:

// Define your layers (typically in a GameLayers.h file)
namespace Layers {
    constexpr uint16_t PLAYER = 0x0001;  // Bit 0
    constexpr uint16_t ENEMY = 0x0002;  // Bit 1
    constexpr uint16_t PROJECTILE = 0x0004; // Bit 2
    constexpr uint16_t WALL = 0x0008;   // Bit 3
}

// Set up a player actor
player->setCollisionLayer(Layers::PLAYER);
player->setCollisionMask(Layers::ENEMY | Layers::WALL); // Can collide with enemies and walls

// Set up an enemy actor
enemy->setCollisionLayer(Layers::ENEMY);
enemy->setCollisionMask(Layers::PLAYER | Layers::PROJECTILE); // Can collide with player and projectiles

The collision system only checks collisions between actors whose layers and masks overlap. This is much more efficient than checking every pair.

Scene Management

Setting the Active Scene

From your main code, set the active scene:

MyGameScene gameScene;

void setup() {
    engine.init();
    gameScene.init();
    engine.setScene(&gameScene); // Set as active scene
}

Switching Scenes

To switch to a different scene:

MenuScene menuScene;
GameScene gameScene;

void switchToGame() {
    gameScene.init();
    engine.setScene(&gameScene); // Replaces current scene
}

Scene Stack (Push/Pop)

For menus and pause screens, use the scene stack:

// Push a pause menu (game scene stays in background)
void pauseGame() {
    pauseMenu.init();
    engine.getCurrentScene()->getSceneManager().pushScene(&pauseMenu);
}

// Pop the pause menu (resume game)
void resumeGame() {
    engine.getCurrentScene()->getSceneManager().popScene();
}

Note: Scene stack management is handled internally by the Engine's SceneManager. You typically access it through engine.getCurrentScene().

Complete Example

Here's a complete example of a scene with multiple entities:

#include <core/Scene.h>
#include <core/Entity.h>
#include <core/Actor.h>
#include <graphics/Renderer.h>
#include <graphics/Color.h>

// A simple moving entity
class MovingBox : public pixelroot32::core::Entity {
public:
    MovingBox(float x, float y) 
        : Entity(x, y, 20, 20, pixelroot32::core::EntityType::GENERIC),
          speedX(50.0f), speedY(30.0f) {
        setRenderLayer(1);
    }

    void update(unsigned long deltaTime) override {
        float dt = deltaTime * 0.001f; // Convert to seconds

        x += speedX * dt;
        y += speedY * dt;

        // Bounce off screen edges
        if (x < 0 || x > 220) speedX = -speedX;
        if (y < 0 || y > 220) speedY = -speedY;
    }

    void draw(pixelroot32::graphics::Renderer& renderer) override {
        renderer.drawFilledRectangle(
            static_cast<int>(x), 
            static_cast<int>(y), 
            width, 
            height, 
            pixelroot32::graphics::Color::Cyan
        );
    }

private:
    float speedX, speedY;
};

// A simple actor that can collide
class CollidableBox : public pixelroot32::core::Actor {
public:
    CollidableBox(float x, float y)
        : Actor(x, y, 30, 30) {
        setRenderLayer(1);
        setCollisionLayer(0x0001);
        setCollisionMask(0x0001);
    }

    void update(unsigned long deltaTime) override {
        // Static actor, no movement
    }

    void draw(pixelroot32::graphics::Renderer& renderer) override {
        renderer.drawFilledRectangle(
            static_cast<int>(x), 
            static_cast<int>(y), 
            width, 
            height, 
            pixelroot32::graphics::Color::Yellow
        );
    }

    pixelroot32::core::Rect getHitBox() override {
        return {x, y, width, height};
    }

    void onCollision(pixelroot32::core::Actor* other) override {
        // Change color when collided
        // (In a real game, you'd handle collision logic here)
    }
};

// The scene
class ExampleScene : public pixelroot32::core::Scene {
public:
    void init() override {
        // Create and add entities
        addEntity(new MovingBox(50, 50));
        addEntity(new MovingBox(150, 100));
        addEntity(new CollidableBox(100, 100));
    }

    void update(unsigned long deltaTime) override {
        Scene::update(deltaTime); // Update all entities
    }

    void draw(pixelroot32::graphics::Renderer& renderer) override {
        // Draw background
        renderer.drawFilledRectangle(0, 0, 240, 240, 
            pixelroot32::graphics::Color::Black);

        Scene::draw(renderer); // Draw all entities
    }
};

Best Practices

Entity Management

  • Pre-allocate entities: Create entities in init(), not in update()
  • Reuse entities: Instead of creating/destroying, enable/disable entities
  • Limit entity count: MAX_ENTITIES = 32 per scene
  • Use object pooling: For frequently created/destroyed entities (projectiles, particles)

Scene Organization

  • One scene per screen: Menu, game, game over, etc.
  • Keep scenes focused: Each scene should have a single responsibility
  • Initialize in init(): Don't do heavy work in the constructor
  • Clean up properly: Remove entities when switching scenes

Collision Layers

  • Plan your layers: Design your layer system before coding
  • Use bit flags: Makes layer combinations easy
  • Keep it simple: Don't over-complicate with too many layers
  • Document your layers: Create a GameLayers.h file

Common Patterns

Entity Pool Pattern

For entities that are frequently created and destroyed (like projectiles):

class ProjectilePool {
    static const int POOL_SIZE = 10;
    ProjectileActor pool[POOL_SIZE];

public:
    ProjectileActor* getAvailable() {
        for (int i = 0; i < POOL_SIZE; i++) {
            if (!pool[i].isActive) {
                pool[i].isActive = true;
                return &pool[i];
            }
        }
        return nullptr; // Pool exhausted
    }
};

Entity Factory Pattern

Create entities through factory functions:

Entity* createEnemy(EnemyType type, float x, float y) {
    switch (type) {
        case EnemyType::BASIC:
            return new BasicEnemy(x, y);
        case EnemyType::FAST:
            return new FastEnemy(x, y);
        // ...
    }
}

Next Steps

Now that you understand scenes and entities, learn about: - Basic Rendering - Draw sprites, text, and primitives - Input and Control - Handle user input - Physics and Collisions - Advanced collision handling


See also: - Fundamental Concepts - API Reference - Scene - API Reference - Entity - API Reference - Actor