top of page
Search
cedarcantab

Understanding 2D Physics Engines with Phaser 3, Part 14: SAT - Capsule vs Capsule

Updated: May 15, 2023

We saw in the previous post that we can use the separating axis theorem to detect collisions between a polygon and circle, the point being that SAT can deal with shapes with curves, so long as (i) it is convex, and (ii) you know the potential axes of separation to test, and you can find the "vertices" to project.


In the case of polygon vs circle, the possible axes for the circle was one that extended from the center of the circle to the nearest vertex of the polygon.


But can SAT deal with capsule vs capsule? It turns out it can.



The possible axes of separation


To start with the answer, the possible axes to test are:

  1. axis along the "spine" of the capsule;

  2. axis perpendicular to the above

  3. axis which joins the centres of the caps of the two capsules (you dont need to test all - just the shortest one)


In essence, what you need to do is to think of the capsule in 3 parts: 2 x circles at the end, and a "rectangle" between the "caps". The axes for the "rectangle" body part are given by the first 2 - you are essentially taking the normals of the edges, like the polygon vs polygon examples described previously. For the "caps", you are taking the axis which joins the two centres of the "circles".


To find the (3), you need to know the cap centre points of the other capsule. In order to accommodate this the code to get the potential axes of separation, included within the capsule class has become a bit more complicated. As you can see, the cap centre points of the other capsule is passed as a parameter.


Just to be transparent, the code copies heavily from the excellent tutorials by Dyn4j.


  getAxes(foci) {
    let axes = [];
   
    // we need to include the shortest vector from foci to foci
    axes.push(new Phaser.Math.Vector2(this.x2 - this.x1, this.y2 - this.y1).normalize());
    axes.push(new Phaser.Math.Vector2(this.x2 - this.x1, this.y2 - this.y1).normalizeRightHand().normalize());

    let f1 = this.getPointA();
    let f2 = this.getPointB();
    for (let i = 0; i < foci.length; i++) {
      // get the one closest to the given focus
      let d1 = Phaser.Math.Distance.BetweenPointsSquared(f1, foci[i]);
      let d2 = Phaser.Math.Distance.BetweenPointsSquared(f2, foci[i]);
      let v = null;
      if (d1 < d2) {
        v = new Phaser.Math.Vector2(foci[i].x - f1.x, foci[i].y - f1.y);
        }
      else
      {
        v = new Phaser.Math.Vector2(foci[i].x - f2.x, foci[i].y - f2.y);
      }
        v.normalize();
        axes.push(v);
      }
      return axes;
   
  }

To accommodate this, a simple method to get the cap centres has been added to the capsule class as follows.



  getFoci() {
    let foci = [];
    foci.push(this.getPointA());
    foci.push(this.getPointB());
    return foci;
  }

The basic logic for SAT remains the same as before, but a few lines have been added at the beginning to handle the extra bits dealing with the getting of cap centres.




  sat(convex1, convex2, penetration) {
    let n = new Phaser.Math.Vector2();
    let overlap = Number.MAX_SAFE_INTEGER;

    let foci1 = convex1.getFoci();
    let foci2 = convex2.getFoci();

    let axes1 = convex1.getAxes(foci2);
    let axes2 = convex2.getAxes(foci1);

    if (axes1 !== null) {
      let size = axes1.length;
      for (let i = 0; i < size; i++) {
        const axis = axes1[i];
        const intervalA = convex1.projectVertices(axis);
        const intervalB = convex2.projectVertices(axis);
        // if the intervals do not overlap then the two shapes
        // cannot be intersecting
        if (!intervalA.overlaps(intervalB)) {
          // the shapes cannot be intersecting so immediately return null
          return false;
        } else {
          // get the overlap
          let o = intervalA.getOverlap(intervalB);
          
          ...................rest essentially the same as before

And that is essentially it.


Here is the Codepen





Key references




Comments


記事: Blog2_Post
bottom of page