0
\$\begingroup\$

For the 2D game that I'm building, I want some entities to be able to use areas of attack, such as a wedge/circle sector area of attack.

In this case, the circle sector is a pie-slice shaped section of a circle defined by:

  • The circle centre coordinates
  • The circle radius
  • The angle of the slice's centre
  • The angle size of the slice

With this information, I can easily detect if a point is contained inside the sector.

However, I want to test for collisions with:

  • Other circle sectors
  • Circles (2D)
  • Rectangles (2D)

How can I do this?

\$\endgroup\$

2 Answers 2

0
\$\begingroup\$

I believe I may have an answer, though please do point out if I'm incorrect. I'll be pasting my answers in TypeScript, leaving out the basic functions that you can find pretty easily on the web.

Wedge-Circle Collision

This appears to be the simplest. Already knowing how to make two circles collide (test their distances from one another is less than the two radiuses), we want to do a similar test for a wedge and a circle, and then determine if the point of collision is inside the wedge:

export function wedgeAndCircleCollide(
  wedge: BoundingWedge,
  circle: BoundingCircle,
): boolean {
  if (!circlesCollide(
    boundingCircleFromWedge(wedge),
    circle,
  )) {
    return false
  }

  // Determine the collision angle between the two circles for
  // the wedge
  const wedgeCollisionAngle = pointAngle(
    wedge.center,
    circle.center,
  )

  // Return whether the angle of collision is within the wedge's
  // angles
  return angleInRange(wedgeCollisionAngle, wedge.directionAngle, wedge.widthAngle)
}

Wedge-Wedge Collision

Similarly, wedge-wedge collision adds a second step of testing if that angle is in the range of the second wedge as well:

export function wedgesCollide(bw1: BoundingWedge, bw2: BoundingWedge): boolean {
  if (!circlesCollide(
    boundingCircleFromWedge(bw1),
    boundingCircleFromWedge(bw2),
  )) {
    return false
  }

  // Determine the collision angle between the two circles for...
  //  ...wedge 1
  const w1CollisionAngle = pointAngle(
    bw1.center,
    bw2.center,
  )

  // ...wedge 2
  const w2CollisionAngle = pointAngle(
    bw2.center,
    bw1.center,
  )

  // If the collision angle for wedge 1 is within wedge 1's
  // angle, mark it down
  const collisionInW1 = angleInRange(w1CollisionAngle, bw1.directionAngle, bw1.widthAngle)

  // If the collision angle for wedge 2 is within wedge 2's
  // angle, mark it down
  const collisionInW2 = angleInRange(w2CollisionAngle, bw2.directionAngle, bw2.widthAngle)

  // Return whether the collision is in both wedge 1 and wedge 2
  return collisionInW1 && collisionInW2
}

Box-Wedge Collision

This is by far the most complicated.

The main function goes something like:

export function wedgeAndBoxCollide(wedge: BoundingWedge, box: BoundingBox) {
  const circleFromWedge = boundingCircleFromWedge(wedge)

  // Cheap check:
  // Check that the circle is inside the rectangle. If so,
  // return true
  if (boundingCircleInBox(circleFromWedge, box)) {
    return true
  }

  const boxCentreX = box.x + (box.width / 2)
  const boxCentreY = box.y + (box height / 2)

  const topLeft: PointTuple = [box.x, box.y]
  const bottomLeft: PointTuple = [box.x, box.y + box.height]
  const topRight: PointTuple = [box.x + box.width, box.y]
  const bottomRight: PointTuple = [box.x + box.width, box.y + box.height]

  const closestHorizontalWall: LineTuple = wedge.center.x <= boxCentreX
    ? [topLeft, bottomLeft]
    : [topRight, bottomRight]

  const closestVerticallWall: LineTuple = wedge.center.y <= boxCentreY
    ? [topLeft, topRight]
    : [bottomLeft, bottomRight]

  // Check the two most relevant walls. If there's a collision,
  // return true
  return wedgeCollidesWithLine(closestHorizontalWall, wedge) ||
    wedgeCollidesWithLine(closestVerticallWall, wedge)
}

where wedgeCollidesWithLine() looks like:

export function wedgeCollidesWithLine(
  line: LineTuple,
  wedge: BoundingWedge,
): boolean {
  const safeDirectionAngle = getSafeAngle(wedge.directionAngle)
  const wedgeHalfAngle = getSafeAngle(wedge.widthAngle) / 2

  if (
    pointInWedge(line[0], wedge) ||
    pointInWedge(line[1], wedge)
  ) {
    return true
  }

  const lineSlope = getSlope(line)
  // y = mx + b
  // b = -mx + y
  const perpendicularLineIntercept = (lineSlope * wedge.center.x * -1) + wedge.center.y
  const perpendicularX = wedge.center.x + 10
  const perpendicularY = (lineSlope * perpendicularX) + perpendicularLineIntercept

  // Point-angle to box edge
  // Return true if in wedge
  const centreToLineIntersectionPoint = getLineIntersectionPoint(
    [ wedge.center, [perpendicularX, perpendicularY] ],
    line,
  )

  // If the distance from the line to the wedge circle
  // is greater than the radius length, there's not going
  // to be an intersection
  if (
    pointDistance(
      wedge.center,
      centreToLineIntersectionPoint,
    ) > wedgeRadius
  ) {
    return false
  }

  if (pointInWedge(centreToLineIntersectionPoint, wedge)) {
    return true
  }

  const safeLeftBound = getSafeAngle(safeDirectionAngle - wedgeHalfAngle)
  const safeRightBound = getSafeAngle(safeDirectionAngle + wedgeHalfAngle)
  // Get intersection by wedge bounds
  // Is it in the given line?
  const leftEdgePoint: PointTuple = [
    wedge.center.x + getLengthDirX(
      safeLeftBound,
      wedge.radius,
    ),
    wedge.center.y + getLengthDirY(
      safeLeftBound,
      wedge.radius,
    ),
  ]
  const leftIntersectionPoint = getLineIntersectionPoint(
    [wedgeCentre, leftEdgePoint],
    line,
  )

  const rightEdgePoint: PointTuple = [
    wedge.center.x + getLengthDirX(
      safeRightBound,
      wedge.radius,
    ),
    wedge.center.y + getLengthDirY(
      safeRightBound,
      wedge.radius,
    ),
  ]
  const rightIntersectionPoint = getLineIntersectionPoint(
    [wedgeCenter, rightEdgePoint],
    line,
  )

  // Check if the intersections with the wedge bounds
  // are within the wedge: if not, the wedge is not facing
  // the line, and thus no collision occurs
  return pointInWedge(leftIntersectionPoint, wedge) ||
    pointInWedge(rightIntersectionPoint, wedge)
}

The idea here being:

  • If there's a collision with a corner, it's a collision
  • For each relevant edge:
    • Get the distance to the edge
    • Get the point-angle between the wedge and the box edge
    • Determine if the line from the wedge to the edge is in the wedge
    • If not, extend the "wings" of the wedge, and see if they collide with the line at all. If they do, determine if that collision is inside the wedge bounds.
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Shape collision problems tend to have lots of non-obvious edge cases. I suspect that at least your wedge-wedge collision is not correct - does it deal with the case of two narrow wedges making a cross shape without the points or edges overlapping? \$\endgroup\$
    – Roy Ward
    Commented Jun 13, 2023 at 7:07
  • \$\begingroup\$ You're absolutely right... neither the wedge-wedge nor wedge-circle account for this. I'll need to take another look with this specifically in mind. This would likely account for some errors I've been seeing so far... \$\endgroup\$ Commented Jun 13, 2023 at 15:04
-1
\$\begingroup\$

I have used this method when I want to my Enemy attack my Player when the Player go into the Enemy's attack area.

using UnityEngine;

public class EnemyCombat : EnemyCombat
{
    public Transform attackPoint;   
    public float attackRange;
    private Collider2D[] hitPlayer;
    public LayerMask playerLayer;
    // Starthas Attackedbefore the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        Attack_FollowCharacter();
    }
    private void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(attackPoint.position, attackRange);   //Use need to use this to visually the area, for this it will draw a circle with the attackPoint is the center of the circle
    }
    private void Attack_FollowCharacter()
    {
            hitPlayer = Physics2D.OverlapCircleAll(attackPoint.position, attackRange, playerLayer);  //Check the Player if they go into attack area
            foreach (Collider2D player in hitPlayer)
            {
                 //Write your code if Player went into
            }
   }

If anything hard to understand few free to respond

\$\endgroup\$
3
  • 2
    \$\begingroup\$ This does not seem to address the "sector" part of the question - it tests against an entire disc, all 360 degrees of it, instead of one pie slice with a limited angle. \$\endgroup\$
    – DMGregory
    Commented May 3, 2023 at 13:25
  • \$\begingroup\$ link you may find solution in this \$\endgroup\$
    – Tien Hung
    Commented May 3, 2023 at 13:49
  • 2
    \$\begingroup\$ This handles drawing an arc, not performing collision/overlap tests on it. \$\endgroup\$
    – DMGregory
    Commented May 3, 2023 at 13:51

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .