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¶
- init(): Called once when the scene is set as active
- Create and initialize entities
- Set up game state
- Load resources
-
Configure palettes, audio, etc.
-
update(deltaTime): Called every frame
- Process input
- Update game logic
- Handle collisions
-
Must call
Scene::update(deltaTime)to update all entities -
draw(renderer): Called every frame
- Draw background elements
- Draw UI elements
- 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 inupdate() - 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.hfile
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