Skip to content

PhysicsActor

An actor with basic 2D physics properties.

Description

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

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

Namespace

namespace pixelroot32::core {
    class PhysicsActor : public Actor {
        // ...
    };
}

Inheritance

  • Inherits from: Actor
  • Inherited by: Your custom physics-enabled actor classes

Constructors

PhysicsActor(float x, float y, float w, float h)

Creates a physics-enabled actor with specified position and size.

Parameters: - x (float): Initial X position in world space - y (float): Initial Y position in world space - w (float): Actor width in pixels - h (float): Actor height in pixels

Notes: - Velocity starts at (0, 0) - Restitution defaults to 1.0 (perfect bounce) - Friction defaults to 0.0 (no friction) - No world limits by default

Example:

class BallActor : public pixelroot32::core::PhysicsActor {
public:
    BallActor(float x, float y) 
        : PhysicsActor(x, y, 8.0f, 8.0f) {
        // Set physics properties
        setRestitution(0.8f);  // 80% bounce
        setFriction(0.1f);     // Small friction
        setWorldSize(128, 128); // World bounds
    }

    void draw(pixelroot32::graphics::Renderer& renderer) override {
        renderer.drawFilledCircle(static_cast<int>(x + width/2), 
                                 static_cast<int>(y + height/2), 
                                 width/2, 
                                 Color::White);
    }

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

    void onCollision(Actor* other) override {
        // Bounce off other actors
    }

    void onWorldCollision() override {
        // Play bounce sound
    }
};

Protected Properties

float vx, vy

Horizontal and vertical velocity components.

Type: float

Access: Protected (use setVelocity() to modify)

Default: 0.0f

Notes: - Velocity is in pixels per second - Automatically applied during update() - Modified by friction and world collisions

Public Methods

void setVelocity(float x, float y)

Sets the linear velocity of the actor.

Parameters: - x (float): Horizontal velocity in pixels per second - y (float): Vertical velocity in pixels per second

Returns: - void

Notes: - Velocity is applied every frame during update() - Use for initial velocity or impulse-based movement - Can be called every frame for continuous control

Example:

// Set initial velocity
physicsActor->setVelocity(100.0f, -200.0f);  // Move right and up

// Continuous control (e.g., player movement)
void update(unsigned long deltaTime) override {
    PhysicsActor::update(deltaTime);

    float speed = 150.0f;
    float vx = 0.0f, vy = 0.0f;

    if (input.isButtonDown(Buttons::LEFT)) vx = -speed;
    if (input.isButtonDown(Buttons::RIGHT)) vx = speed;
    if (input.isButtonDown(Buttons::UP)) vy = -speed;
    if (input.isButtonDown(Buttons::DOWN)) vy = speed;

    setVelocity(vx, vy);
}

void setRestitution(float r)

Sets the restitution (bounciness) of the actor.

Parameters: - r (float): Restitution value (0.0 to 1.0+) - 0.0: No bounce (stops on impact) - 1.0: Perfect bounce (no energy loss) - > 1.0: Energy gain (unrealistic but possible)

Returns: - void

Notes: - Applied when actor collides with world boundaries - Higher values = more bouncy - Typical values: 0.5-0.9 for realistic bouncing

Example:

ball->setRestitution(0.8f);  // 80% bounce

void setFriction(float f)

Sets the friction coefficient.

Parameters: - f (float): Friction value - 0.0: No friction (object continues moving) - > 0.0: Friction applied to velocity each frame

Returns: - void

Notes: - Applied every frame to reduce velocity - Higher values = more friction (slower movement) - Typical values: 0.05-0.2 for smooth deceleration

Example:

player->setFriction(0.1f);  // Light friction

void setLimits(LimitRect limits)

Sets custom movement limits for the actor.

Parameters: - limits (LimitRect): A rectangle defining the allowed area

Returns: - void

Notes: - Overrides world size limits - Use -1 for any boundary to disable that limit - Actor will bounce off these boundaries

Example:

pixelroot32::core::LimitRect limits;
limits.left = 0;
limits.top = 0;
limits.right = 128;
limits.bottom = 128;
physicsActor->setLimits(limits);

void setWorldSize(int width, int height)

Defines the world size for boundary checking.

Parameters: - width (int): Width of the world in pixels - height (int): Height of the world in pixels

Returns: - void

Notes: - Used as default limits if no custom LimitRect is provided - Actor will bounce off world boundaries - Set to display size for screen boundaries

Example:

physicsActor->setWorldSize(128, 128);  // Match display size

WorldCollisionInfo getWorldCollisionInfo() const

Gets information about collisions with the world boundaries.

Returns: - WorldCollisionInfo: A struct containing collision flags (left, right, top, bottom)

Notes: - Updated every frame during update() - Use to detect which boundary was hit - Useful for sound effects or special behaviors

Example:

void update(unsigned long deltaTime) override {
    PhysicsActor::update(deltaTime);

    auto collision = getWorldCollisionInfo();
    if (collision.left || collision.right) {
        // Hit side wall
        playSound(wallHitSound);
    }
    if (collision.top || collision.bottom) {
        // Hit top or bottom
        playSound(ceilingHitSound);
    }
}

virtual void onCollision(Actor* other) override

Callback triggered when this actor collides with another actor.

Parameters: - other (Actor*): Pointer to the actor involved in the collision

Returns: - void

Notes: - Called automatically by the collision system - Override to implement custom collision responses - Default implementation does nothing

Example:

void onCollision(Actor* other) override {
    if (other->isInLayer(DefaultLayers::kEnemy)) {
        // Bounce off enemy
        vx = -vx * 0.5f;
        vy = -vy * 0.5f;
    }
}

virtual void onWorldCollision()

Callback triggered when this actor collides with world boundaries.

Returns: - void

Notes: - Called automatically when a world boundary collision occurs - Override to implement custom behavior (sound effects, particles, etc.) - Default implementation does nothing

Example:

void onWorldCollision() override {
    // Play bounce sound
    auto& audio = engine.getAudioEngine();
    pixelroot32::audio::AudioEvent sound{};
    sound.type = pixelroot32::audio::WaveType::NOISE;
    sound.frequency = 500.0f;
    sound.duration = 0.05f;
    audio.playEvent(sound);

    // Spawn particles
    spawnBounceParticles();
}

void update(unsigned long deltaTime) override

Updates the actor state. Applies physics integration and checks for world boundary collisions.

Parameters: - deltaTime (unsigned long): Time elapsed since the last frame in milliseconds

Returns: - void

Notes: - Called automatically by Scene if isEnabled is true - Applies velocity to position - Applies friction to velocity - Resolves world boundary collisions - Override to add custom update logic, but call PhysicsActor::update(deltaTime) first

Example:

void update(unsigned long deltaTime) override {
    // Apply physics
    PhysicsActor::update(deltaTime);

    // Custom logic
    if (shouldApplyGravity) {
        vy += gravity * (deltaTime / 1000.0f);
    }
}

LimitRect Structure

Bounding rectangle for world-collision resolution.

Members: - int left: Left boundary (-1 means no limit) - int top: Top boundary (-1 means no limit) - int right: Right boundary (-1 means no limit) - int bottom: Bottom boundary (-1 means no limit)

Methods: - int width() const: Calculates width (right - left) - int height() const: Calculates height (bottom - top)

Example:

pixelroot32::core::LimitRect limits(10, 10, 118, 118);  // 10px margin
physicsActor->setLimits(limits);

WorldCollisionInfo Structure

Information about world collisions in the current frame.

Members: - bool left: True if collided with the left boundary - bool right: True if collided with the right boundary - bool top: True if collided with the top boundary - bool bottom: True if collided with the bottom boundary

Example:

auto collision = physicsActor->getWorldCollisionInfo();
if (collision.bottom) {
    // On ground
    canJump = true;
}

Usage Example

#include "core/PhysicsActor.h"

class BouncingBall : public pixelroot32::core::PhysicsActor {
public:
    BouncingBall(float x, float y) 
        : PhysicsActor(x, y, 8.0f, 8.0f) {
        // Set physics properties
        setRestitution(0.9f);  // Very bouncy
        setFriction(0.05f);    // Light friction
        setWorldSize(128, 128);

        // Set initial velocity
        setVelocity(100.0f, -150.0f);

        // Set collision layer
        layer = pixelroot32::physics::DefaultLayers::kProjectile;
        mask = pixelroot32::physics::DefaultLayers::kObstacle;
    }

    void update(unsigned long deltaTime) override {
        PhysicsActor::update(deltaTime);

        // Apply gravity
        vy += 200.0f * (deltaTime / 1000.0f);
    }

    void draw(pixelroot32::graphics::Renderer& renderer) override {
        renderer.drawFilledCircle(static_cast<int>(x + width/2), 
                                 static_cast<int>(y + height/2), 
                                 width/2, 
                                 Color::White);
    }

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

    void onCollision(Actor* other) override {
        // Bounce off obstacles
        vx = -vx * 0.8f;
        vy = -vy * 0.8f;
    }

    void onWorldCollision() override {
        // Play bounce sound
        playBounceSound();
    }
};

Performance Considerations

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

ESP32 Considerations

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

See Also