Code Samples

~ 2D Axis-Aligned Bounding Box vs. Triangle Collision Detection using the Separating Axis Theorem

bool AABBvsTriangle(Manifold& m)
{
   AABB_t* a = m.a->asAABB();
   Triangle* b = m.b->asTriangle();

   bool hypotenuse_case = collide_hyp(*b, m.b_p, m.a_p);

   // test for projection overlap along the x-axis
   float dist_x = m.a_p.x - m.b_p.x;
   float a_proj = scalar_proj(a->xw, X_AXIS); // + scalar_proj(a->yw, X_AXIS);
   float b_proj, overlap_x;
   // determine the relative position of AABB to Triangle on the x-axis
   if ((b->xw.x > 0 && dist_x < 0) || // b faces (+) and a is (-) of b
       (b->xw.x < 0 && dist_x > 0))   // b faces (-) and a is (+) of b
   {
      // hypotenuse opposite from AABB along x-axis
      overlap_x = a_proj - std::abs(dist_x);
   }
   else { // hypotenuse adjacent to AABB along y-axis
      // float b_halfwidth_x = b->xw.x / 2; // NOTE: changed b->xw.x to halfwidth 
      dist_x = m.a_p.x - (m.b_p.x + b->xw.x);
      overlap_x = a_proj + std::abs(b->xw.x) - std::abs(dist_x);
   }
   if (overlap_x > 0)
   {
      // test for projection overlap along the y-axis
      float dist_y = m.a_p.y - m.b_p.y;
      a_proj = /* scalar_proj(a->xw, Y_AXIS) + */ scalar_proj(a->yw, Y_AXIS);
      float overlap_y;
      // determine the relative position of AABB to Triangle on the y-axis
      if ((b->yw.y > 0 && dist_y < 0) || // b faces (+) and a is (-) of b
          (b->yw.y < 0 && dist_y > 0))   // b faces (-) and a is (+) of b
      {
         // hypotenuse opposite from AABB along x-axis
         overlap_y = a_proj - std::abs(dist_y);
      }
      else { // hypotenuse adjacent to AABB along y-axis
         // float b_halfwidth_y = b->yw.y / 2; // NOTE: changed b->yw.y to halfwidth 
         dist_y = m.a_p.y - (m.b_p.y + b->yw.y);
         overlap_y = a_proj + std::abs(b->yw.y) - std::abs(dist_y);
      }
      if (overlap_y > 0)
      {
         if (!hypotenuse_case)
         {
            // project out of collision along the axis of least overlap
            if (overlap_x < overlap_y) {
               if (dist_x > 0) { 
                  m.projection = vec2_t(overlap_x, 0); // x-axis is min
               }
               else {
                  m.projection = vec2_t(-overlap_x, 0);
               }
            }
            else {
               if (dist_y > 0) {
                  m.projection = vec2_t(0, overlap_y); // y-axis is min 
               }
               else {
                  m.projection = vec2_t(0, -overlap_y);
               }
            }

            return true;
         }
         else // special case for when hypotenuse lies between the two centers
         {
            // test for projection overlap along the 3rd separating axis
            float dist_third =
               (b->third_axis.x * (m.b_p.x - m.a_p.x)) 
               + (b->third_axis.y * (m.b_p.y - m.a_p.y));

            // 3rd axis may contain negatives, so we need to use magnitude for the AABB
            a_proj = std::abs(scalar_proj(a->xw, b->third_axis)) + 
                     std::abs(scalar_proj(a->yw, b->third_axis));

            // both legs are used bc they are halfwidths
            b_proj = scalar_proj(b->xw, b->third_axis) 
                     + scalar_proj(b->yw, b->third_axis);

            float overlap_third = a_proj + b_proj - std::abs(dist_third);

            if (overlap_third > 0)
            {
               // project out of collision along the axis of least overlap
               if (overlap_x < overlap_y) {
                  if (overlap_x < overlap_third) {
                     if (dist_x > 0) {
                        m.projection = vec2_t(overlap_x, 0); // x- axis is min
                     }
                     else {
                        m.projection = vec2_t(-overlap_x, 0);
                     }
                  } 
                  else { // third axis is min
                     m.projection = 
                        vec2_t(b->third_axis.x * overlap_third, 
                               b->third_axis.y * overlap_third);
                  }
               }
               else {
                  if (overlap_y < overlap_third) {
                     if (dist_y > 0) {
                        m.projection = vec2_t(0, overlap_y); // y-axis is min   
                     }
                     else {
                        m.projection = vec2_t(0, -overlap_y);
                     }
                  } 
                  else { // third axis is min
                     m.projection = 
                        vec2_t(b->third_axis.x * overlap_third, 
                               b->third_axis.y * overlap_third);
                  }
               }

               return true;
            }
         }
      }
   }

   return false;
}

struct Manifold
{
   PhysObj* a; // unowned pointer to a
   PhysObj* b; // unowned pointer to b
   vec3_t a_p; // position of a
   vec3_t b_p; // position of b

   bool resolve = false;
   vec2_t projection;
   material_t material;
};

~ Executing a Shootout in Standoff

/* For each "armed" piece in play, mark the enemy piece closest to the shooter for
 * removal, accounting for both friendly obstacles to line-of-sight and piece's 
 * with two hit rays (slingers)
 */
void Game_c::shootout()
{
   std::vector<PiecePtr> live_pieces;
   std::vector<PiecePtr> hit_pieces;

   std::vector<PiecePtr>::iterator p1_it;
   for (p1_it = mPlayer1Pieces.begin(); p1_it != mPlayer1Pieces.end(); ++p1_it)
   {
      if ((*p1_it)->getPlayState() == Piece_n::LIVE)
      {
         live_pieces.push_back(*p1_it);
      }
   }

   std::vector<PiecePtr>::iterator p2_it;
   for (p2_it = mPlayer2Pieces.begin(); p2_it != mPlayer2Pieces.end(); ++p2_it)
   {
      if ((*p2_it)->getPlayState() == Piece_n::LIVE)
      {
         live_pieces.push_back(*p2_it);
      }
   }

   std::vector<PiecePtr>::iterator it;
   for (it = live_pieces.begin(); it != live_pieces.end(); ++it)
   {
      if ((*it)->getPieceType() != Piece_n::PAWN)
      {
         Piece_n::Direction_e direction = (*it)->getDirection();
         detectHit2(**it, direction, live_pieces);

         if ((*it)->getPieceType() == Piece_n::SLINGER)
         {
            Piece_n::Direction_e secondary_direction;
            switch(direction)
            {
               case Piece_n::UP : { secondary_direction = Piece_n::RIGHT; break; }
               case Piece_n::DOWN : { secondary_direction = Piece_n::LEFT; break; }
               case Piece_n::LEFT : { secondary_direction = Piece_n::UP; break; }
               case Piece_n::RIGHT : { secondary_direction = Piece_n::DOWN; break; }
            }
            detectHit2(**it, secondary_direction, live_pieces);
         }
      }
   }
}

void Game_c::detectHit(Piece_n::Piece_c& piece, 
                       Piece_n::Direction_e direction,
                       std::vector<PiecePtr>& pieces)
{
   // true once if we have already hit a piece (monodirectional)
   bool hit_piece_flag = false;

   // stores the hit piece closest to the shooter
   PiecePtr hit_piece;

   switch (direction)
   {
      case Piece_n::UP :
      {
         std::vector<PiecePtr>::iterator it;
         for (it = pieces.begin(); it != pieces.end(); ++it)
         {
            if ((*it)->getPlayState() == Piece_n::LIVE)
            {
               if ((*it)->getPosition().first == piece.getPosition().first &&
                   (*it)->getPosition().second < piece.getPosition().second)
               {
                  if (hit_piece_flag)
                  {
                     if ((*it)->getPosition().second > hit_piece->getPosition().second)
                     {
                        hit_piece = *it;
                     }
                  }
                  else
                  {
                     hit_piece = *it;
                     hit_piece_flag = true;
                  }
               }
            }
         }
         break;
      }
      // ... case for each other possible piece direction: DOWN, LEFT, RIGHT
   }
   if (piece.getTeam() != hit_piece->getTeam())
   {
      hit_piece->nextPlayState();
   }
}