Why does my implementation of "is 2D point inside 2D polygon" have slightly different results on either side of the same axis?

1
votes

Posted by: sneakyRedPanda on 07/26/2017 | Add Revision

My 2D game is tile-based, 16 pixels per tile. Colliders are defined by an array of points which determine a polygon. With debugging enabled (as it is in the photos) those colliders are outlined with red lines. You can see this on the player and on the water tiles I am using to explain the issue.

The following two photos are both what happens when the player moves leftwards into the collider of the water tile.

enter image description here enter image description here The difference between these two photos is the fact that the player's collider collides with the tile collider on its corner. The second picture represents the desired effect. I should clarify that in the photos, the second (lower) water tile is not required to be there to have the same effect.

Currently this issue only happens when approaching a tile collider moving upwards or leftwards. This has the effect of preventing the player from moving along the tile into a new tile - the corner of the adjacent tile catches the player's collider and stops movement.

Here is the code I'm using to determine if a point is within a polygon. This function is called on each point that makes up the collider of the player.

def self.point_in_poly(testx, testy, *poly)
  return false if outside_bounding_box?(testx, testy, *poly)

  point_in_poly = false
  i = -1
  j = poly.count - 1
  while (i += 1) < poly.count
    a_point_on_polygon = poly[i]
    trailing_point_on_polygon = poly[j]
    if point_is_between_the_ys_of_the_line_segment?([testx, testy], a_point_on_polygon, trailing_point_on_polygon)
      if ray_crosses_through_line_segment?([testx, testy], a_point_on_polygon, trailing_point_on_polygon)
        point_in_poly = !point_in_poly
      end
    end
    j = i
  end
  return point_in_poly
end

private

def self.point_is_between_the_ys_of_the_line_segment?(point, a_point_on_polygon, trailing_point_on_polygon)
  (a_point_on_polygon[1] <= point[1] && point[1] < trailing_point_on_polygon[1]) || 
  (trailing_point_on_polygon[1] <= point[1] && point[1] < a_point_on_polygon[1])
end

def self.ray_crosses_through_line_segment?(point, a_point_on_polygon, trailing_point_on_polygon)
  (point[0] < (trailing_point_on_polygon[0] - a_point_on_polygon[0]) * (point[1] - a_point_on_polygon[1]) / 
             (trailing_point_on_polygon[1] - a_point_on_polygon[1]) + a_point_on_polygon[0])
end

def self.outside_bounding_box?(x, y, *poly)
  max_x, max_y, min_x, min_y = nil
  x_coords = poly.map {|p| p[0]}
  y_coords = poly.map {|p| p[1]}

  max_x = x_coords.max
  max_y = y_coords.max
  min_x = x_coords.min
  min_y = y_coords.min

  return x < min_x || x > max_x || y < min_y || y > max_y
end

Any insight as to why this might be happening at only at leftwards and upwards directions is appreciated.

Visit Website