Personal Project

2D Physics Simulation

A Java simulation with a custom 2D physics engine, visualizing collision detection and response between simple shapes.

JavaPhysicsCollision Detection

Challenge

Explore game physics by detecting collisions and resolving overlaps between shapes in a real-time simulation.

My contribution

I built a custom 2D physics engine in Java and a visual simulation of object interactions.

Approach

Work with circle-distance checks, shape projections, and separating axes to understand collision detection and response.

Outcome

A visual learning project demonstrating how simple shapes interact, with implementation examples covering circles, AABB, and SAT.

Collision Detection in Practice

The engine checks for collisions between objects every frame. For circles, it uses the distance between centers; for rectangles, it uses axis-aligned bounding box (AABB) checks. When a collision is detected, the engine calculates the overlap and applies a response so the objects bounce off each other realistically.

This approach allows for fast and accurate detection, making it suitable for real-time applications like games or interactive demos.

Theory: How Collision Detection Works

AABB Collision

AABB Collision

Rectangle (AABB) vs. Rectangle: Two rectangles collide if their projections on both the X and Y axes overlap. This is known as Axis-Aligned Bounding Box (AABB) collision detection. AABB is very fast because it only requires comparing the min and max values of the rectangles on each axis. It works best for rectangles that are not rotated.

PseudoCode
collision: just compare min/max on X and Y
  boolean aabbCollide(Rect a, Rect b) {
    return a.x < b.x + b.width &&
        a.x + a.width > b.x &&
        a.y < b.y + b.height &&
        a.y + a.height > b.y;
  }

SAT Collision

SAT Collision

SAT (Separating Axis Theorem): SAT is a more advanced algorithm that can detect collisions between any convex polygons, including rotated rectangles. It works by projecting the shapes onto potential separating axes and checking for overlaps. If a separating axis is found where the projections do not overlap, the shapes are not colliding. SAT is more flexible but also more computationally expensive than AABB.

Pseudocode
collision:
  boolean satCollide(Polygon polyA, Polygon polyB) {
    for (Vector axis : getAxes(polyA, polyB)) {
      Projection projA = project(polyA, axis);
      Projection projB = project(polyB, axis);
      if (!projA.overlaps(projB)) {
        return false;
      }
    }
    return true;
  }

Circle Collision

Circle Collision

Circle vs. Circle: Two circles collide if the distance between their centers is less than the sum of their radii. This is calculated using the Pythagorean theorem.

How SAT and AABB Are Used Together

In this project, SAT (Separating Axis Theorem) is used for accurate collision detection between all shapes, including rotated rectangles and polygons. SAT determines if two objects will collide by checking for a separating axis between them.

Once a collision is detected with SAT, AABB (Axis-Aligned Bounding Box) logic is used to calculate the collision resolution, specifically, to determine the separation axis and penetration depth for resolving overlaps. This approach combines the accuracy of SAT for detection with the simplicity and efficiency of AABB for resolving collisions, especially for axis-aligned objects.

This method ensures that the simulation is both robust (able to handle complex shapes) and efficient (fast resolution for common cases), matching the real implementation in the code.

Code Examples

AABB-like projection (Java)
public boolean overlapsOnAxis(Collider other, Vector2 axis) {
    float[] projA = this.projectOntoAxis(axis);
    float[] projB = other.projectOntoAxis(axis);
    return !(projA[1] < projB[0] || projB[1] < projA[0]);
}

public float[] projectOntoAxis(Vector2 axis) {
    ArrayList<Vector2> worldPoints = getWorldPoints();
    if (worldPoints.isEmpty()) {
        return new float[]{0f, 0f};
    }

    float min = (float) worldPoints.get(0).dot(axis);
    float max = min;

    for (int i = 1; i < worldPoints.size(); i++) {
        float projection = (float) worldPoints.get(i).dot(axis);
        min = Math.min(min, projection);
        max = Math.max(max, projection);
    }

    return new float[]{min, max};
}
Circle-Circle Collision (Java)
public boolean overlapsWithCircle(CircleCollider other) {
    Vector2 thisCenter = this.getObject().futureTransform.position;
    Vector2 otherCenter = other.getObject().futureTransform.position;
    float distance = (float) thisCenter.distance(otherCenter);
    return distance < (this.radius + other.radius);
}
SAT Collision(Java, simplified)
public boolean intersects(Collider other) {
        if (other == null) {
            return false;
        }

        ArrayList<Vector2> axesA = this.getAxes();
        ArrayList<Vector2> axesB = other.getAxes();

        if (axesA.isEmpty() || axesB.isEmpty()) {
            return false;
        }

        for (Vector2 axis : axesA) {
            if (axis.magnitude() < 0.001f) continue;
            if (!this.overlapsOnAxis(other, axis)) {
                return false;
            }
        }

        for (Vector2 axis : axesB) {
            if (axis.magnitude() < 0.001f) continue;
            if (!this.overlapsOnAxis(other, axis)) {
                return false;
            }
        }

        return true;
    }

Keep exploring

See my other projects.

From professional work to personal experiments.

View all projects