James Randall Musings on software development, business and technology.
Elite: fix for laser collisions

Ok. So here’s the revised collision detection code:

function processLaserHits(game: Game, resources: Resources) {
  // all we are really interested in for deciding if a player has hit a ship is the intersection of the bounding
  // box of the ship onto a 2d plane. To do this we take each face of the bounding box, discard the z, and look to see
  // if our laser axis (represented by point [0,0]) falls within it by breaking the face into two and using
  // barycentric co-ordinates to see if we are in the triangle

  const hit = game.localBubble.ships.reduce((hit: ShipInstance | null, ship) => {
    if (ship.position[2] > 0) return hit
    if (hit !== null && hit.position[2] > ship.position[2]) return hit

    const translatedBoundingBox = ship.boundingBox.map((v) => {
      const v2 = vec3.add(vec3.create(), v, ship.position)
      return vec2.fromValues(v2[0], v2[1])
    })
    // This depends very much on the order that the bounding box is created in
    const faces = [
      // front
      [0, 1, 2, 3],
      // back
      [4, 5, 6, 7],
      // left
      [0, 3, 4, 7],
      // right
      [1, 2, 5, 6],
      // top
      [0, 1, 5, 6],
      // bottom
      [2, 3, 6, 7],
    ]
    const testPoint = vec2.fromValues(0, 0)
    for (let fi = 0; fi < faces.length; fi++) {
      const face = faces[fi]
      const quad = [
        translatedBoundingBox[face[0]],
        translatedBoundingBox[face[1]],
        translatedBoundingBox[face[2]],
        translatedBoundingBox[face[3]],
      ]
      const triangles = createTrianglesFromQuad(quad)
      if (isPointInTriangle(testPoint, triangles[0]) || isPointInTriangle(testPoint, triangles[1])) {
        return ship
      }
    }

    return hit
  }, null)
  if (hit === null) {
    resources.soundEffects.playerLaserMiss()
  } else {
    hit.isDestroyed = true
    resources.soundEffects.shipExplosion()
  }
}

Its similar to the previous version but is based around hit detecting each face of the cube in XY space rather than trying to fit a quad around the bounding box (which actually makes no sense at all… what was I thinking, or dreaming!).

There’s definitely some scope for improvement in the above but it does, at least, work!