CollisionSystem¶
Manages physics simulation and collision detection using the "Flat Solver" architecture.
Description¶
CollisionSystem implements the engine's physics pipeline. Unlike simple collision checkers, it provides a full rigid body simulation with gravity, spatial partitioning, and iterative resolution.
It supports three physics body types (configured via PhysicsActor):
- Static (
PhysicsBodyType::STATIC): Immovable world geometry (infinite mass). - Kinematic (
PhysicsBodyType::KINEMATIC): Moved by game logic (platforms, characters). - Rigid (
PhysicsBodyType::RIGID): Fully simulated physics objects (debris, bouncing props).
Namespace¶
Inheritance¶
- Base class: None (standalone class)
- Used by:
Scene(manages collision system instance)
Public Methods¶
void addEntity(Entity* e)¶
Adds an entity to the physics system.
Parameters:
e(pixelroot32::core::Entity*): Pointer to the entity to add
Notes:
- Only
Actorentities participate in physics. - Entities are automatically added when added to
Scene.
void removeEntity(Entity* e)¶
Removes an entity from the physics system.
Parameters:
e(pixelroot32::core::Entity*): Pointer to the entity to remove
void update()¶
Executes the full physics step for the current frame.
Returns:
void
Notes:
- Called automatically by
Scene::update(). - Executes the "Flat Solver" pipeline: Gravity -> Broadphase -> Narrowphase -> Velocity Resolution -> Integration -> Baumgarte.
How It Works (The Flat Solver Pipeline)¶
The physics step is executed in a sequential pipeline every frame using a fixed timestep (FIXED_DT = 1/60s):
- Apply Gravity: Add gravitational velocity to rigid bodies (
v = v + g * dt). - Detect Collisions: Identify all overlapping pairs using the Broadphase (Spatial Grid).
- Solve Velocity: Apply impulse-based collision response to resolve approaching velocities.
- Restitution is calculated as
min(restitutionA, restitutionB)only if both actors havebounce=true. - If either actor has
bounce=false, restitution is 0. - Impacts below
VELOCITY_THRESHOLD(0.5) have 0 restitution to prevent micro-bouncing. - Integrate Positions: Update positions based on velocity (
p = p + v * dt). - Solve Penetration: Apply Baumgarte stabilization (position correction) to fix remaining overlaps.
- Trigger Callbacks: Notify gameplay code via
onCollision().
This order is critical: - Gravity is applied first to ensure correct trajectory. - Velocity is solved before position integration (prevents energy loss). - Position integration happens before penetration correction (allows proper separation). - Callbacks happen last so gameplay sees the final state.
Spatial Partitioning¶
To avoid $O(N^2)$ checks, the engine uses a Uniform Spatial Grid.
- Cell Size: Configurable via
SPATIAL_GRID_CELL_SIZE(default 32px). - Optimization: On ESP32, the grid uses Static Shared Buffers to reclaim ~100KB of DRAM, as the grid is cleared and rebuilt every frame.
Usage Example¶
#include "physics/CollisionSystem.h"
#include "core/Actor.h"
#include "physics/StaticActor.h"
#include "physics/RigidActor.h"
class GameScene : public pixelroot32::core::Scene {
public:
void init() override {
using pixelroot32::math::toScalar;
// 1. Create a Static Floor
auto floor = std::make_unique<pixelroot32::physics::StaticActor>(toScalar(0), toScalar(200), 240, 40);
addEntity(floor.get());
entities.push_back(std::move(floor));
// 2. Create a Dynamic Box (RigidActor)
auto box = std::make_unique<pixelroot32::physics::RigidActor>(toScalar(100), toScalar(50), 20, 20);
box->setRestitution(toScalar(0.5f)); // Bounciness
addEntity(box.get());
entities.push_back(std::move(box));
// Physics is handled automatically by Scene::update()
}
};
Performance Considerations¶
- Use StaticActors: They are significantly cheaper than dynamic bodies.
- Limit Rigid Bodies: On ESP32-C3, aim for <20 simultaneous
RigidActorobjects for stable 60 FPS. - Adjust Cell Size: Match
SPATIAL_GRID_CELL_SIZEto your average actor size for best broadphase performance. - Shape Costs:
AABB vs AABB: Cheapest.Circle vs AABB: Slightly more expensive (sqrt operations).
Continuous Collision Detection (CCD)¶
Automatic CCD for fast-moving circles to prevent tunneling.
When It Activates¶
CCD is used only when necessary to save performance:
- Shape Check: Only for
CIRCLEshapes. - Speed Check: Activates when
velocity * dt > radius * CCD_THRESHOLD.
// Default CCD_THRESHOLD = 3.0
// Example: Ball with radius 6px
// CCD activates when speed > 1080 px/s (6 * 3 / (1/60))
Algorithm¶
The system uses a swept test (swept Circle vs AABB) that samples positions along the movement vector to find the exact time of impact.
Configuration¶
The system is configurable via EngineConfig.h or build flags:
PHYSICS_MAX_PAIRS: Max collisions tracked (Default: 128).VELOCITY_ITERATIONS: Impulse solver iterations per frame. Higher values improve stacking stability (Default: 2).SPATIAL_GRID_CELL_SIZE: Grid cell size (Default: 32).CCD_THRESHOLD: Threshold for activating Continuous Collision Detection (Default: 3.0).BIAS: Baumgarte stabilization factor (Default: 0.2).SLOP: Penetration allowance (Default: 0.02).