13. Rotated Box Collisions
Our current collision system works perfectly for walls that run perfectly horizontal or vertical. But what about slanted walls, rotated doors, or diagonal barriers? Real game worlds need more flexibility than axis-aligned rectangles can provide.
This brings us to Oriented Bounding Boxes (OBBs) - rectangles that can be rotated to any angle whilst maintaining efficient collision detection.
The Fractal Code Cycle Applied to Rotated Collisions
Recall our fundamental pattern:
Input → Processing → Output
For rotated collision detection:
- Input: Entity positions, movement vectors, oriented bounding box data (position, size, rotation)
- Processing: Transform coordinates, perform collision detection in local space, transform results back
- Output: Valid positions accounting for arbitrary rectangle orientations
What Are Rotated Collisions Good For?
Natural Level Design
- Slanted walls and barriers
- Rotated doorways and passages
- Diagonal platforms and ramps
Realistic Environments
- Buildings at angles
- Natural rock formations
- Organic-feeling level geometry
Gameplay Mechanics
- Rotating platforms
- Angled conveyor belts
- Dynamic obstacle orientation
The Coordinate Transformation Approach
Rather than developing complex new collision algorithms, we can use a clever mathematical trick: transform the collision into a space where our existing algorithms work perfectly.
The Big Idea
Instead of checking if a circle collides with a rotated rectangle, we:
- Transform both objects into a coordinate system where the rectangle becomes axis-aligned
- Use our existing circle-vs-rectangle collision detection
- Transform the result back to world coordinates
This approach leverages coordinate system transformations - moving between different mathematical "viewpoints" of the same geometry.
Oriented Bounding Boxes (OBB)
An OBB extends our previous rectangle concept:
OBB :: struct {
position: Vec2, // Centre point
size: Vec2, // Width and height
rotation: f32, // Rotation in radians
debug_color: rl.Color, // For visual debugging
}
This gives us all the data needed to represent and work with rotated rectangles.
Matrix Transformations
We use 3x3 matrices to perform the coordinate transformations:
Translation Matrix (Moving to Origin)
translate_to_origin := Mat3{
1, 0, -obb.position.x,
0, 1, -obb.position.y,
0, 0, 1,
}
This moves the OBB so its centre is at the origin (0, 0).
Rotation Matrix (Undoing Rotation)
undo_rotation := Mat3{
math.cos(-obb.rotation), -math.sin(-obb.rotation), 0,
math.sin(-obb.rotation), math.cos(-obb.rotation), 0,
0, 0, 1,
}
This rotates everything by the negative of the OBB's rotation, making the OBB axis-aligned.
Combined Transformation
world_to_local := undo_rotation * translate_to_origin
Matrix multiplication combines these transformations into a single operation that converts world coordinates to the OBB's local coordinate system.
The Algorithm Step-by-Step
Transform Player to Local Space
player_world := Vec3{player.position.x, player.position.y, 1} player_local := world_to_local * player_worldCreate Local AABB
local_aabb := Rect{-obb.size.x / 2, -obb.size.y / 2, obb.size.x, obb.size.y}Perform Standard Collision Detection
if rl.CheckCollisionCircleRec(player_local.xy, player.collider_radius, local_aabb) { collision_response_local := circle_vs_rect_response(player_local.xy, player.collider_radius, local_aabb) }Transform Result Back to World Space
restore_rotation := Mat3{ math.cos(obb.rotation), -math.sin(obb.rotation), obb.position.x, math.sin(obb.rotation), math.cos(obb.rotation), obb.position.y, 0, 0, 1, } response_world := restore_rotation * response_local
Why Use Homogeneous Coordinates?
You might notice we're using Vec3 with matrices instead of Vec2. This is because we're using homogeneous coordinates, where 2D points are represented as 3D vectors with the third component set to 1.
This mathematical convention allows us to represent both translation and rotation in a single matrix operation, simplifying our coordinate transformations.
Performance Considerations
Matrix operations might seem expensive, but for our scale (dozens of entities), the computational cost is negligible. The benefits of code reuse and correctness far outweigh the minimal performance overhead.
For games with thousands of collision checks per frame, optimised OBB-vs-circle algorithms exist, but they're significantly more complex to implement and debug.
Visual Debugging
Our implementation includes visual debugging that shows:
- The rotated rectangles in their world positions
- The collision boundaries
- The collision response positions
This makes it much easier to verify that our transformations are working correctly and to tune collision behaviour.
Rotated Collisions in Our System Stack
This enhancement builds upon our existing collision system in the World Layer:
- Dependencies: Still uses entities, input, and time from the base layer
- Enhancement: Extends collision detection to handle arbitrary orientations
- Integration: Maintains the same interface with other systems
The beauty of this approach is that it extends our existing system without breaking compatibility.
The Big Idea
Coordinate system transformations are a powerful mathematical tool that allows us to solve complex problems by converting them into simpler, well-understood problems.
By transforming rotated collision detection into axis-aligned collision detection, we leverage existing, working code whilst supporting much more flexible level design.
This principle - "transform the problem into one you already know how to solve" - applies far beyond collision detection and is a valuable problem-solving strategy in game development and programming in general.