Collision detection of a ball with an arc

2020-04-30 02:01发布

问题:

I am making a simple game in which i have a ball and an arc revolving around the center. When user touches the screen the ball moves in the direction of pointer and hits the arc. But i am not able to find any method to detect that collision An image has been attached for better understanding

Game image

Debug Snapshot..

I have a circle bound around my ball... What i am doing is

Detecting the point of intersection of ball center and circle on which arc is >revolving. But i am not able to detect whether the arc was there when ball intersected the circle?? please help...:'(

Code for making arc:

 public void arc (float x, float y, float radius, float start, float degrees,int segments) {
  //  int segments = (int)(6 * (float)Math.cbrt(radius) * (degrees / 360.0f));

    if (segments <= 0) throw new IllegalArgumentException("segments must be > 0.");
    float colorBits = color.toFloatBits();
    float theta = (2 * MathUtils.PI * (degrees / 360.0f)) / segments;
    float cos = MathUtils.cos(theta);
    float sin = MathUtils.sin(theta);
    float cx = radius * MathUtils.cos(start * MathUtils.degreesToRadians);
    float cy = radius * MathUtils.sin(start * MathUtils.degreesToRadians);

    for (int i = 0; i < segments; i++) {
        renderer.color(colorBits);
        Gdx.gl20.glLineWidth(10);
        Gdx.gl.glEnable(GL20.GL_BLEND);
        renderer.vertex(x + cx, y + cy, 0);
        float temp = cx;
        cx = cos * cx - sin * cy;
        cy = sin * temp + cos * cy;
        renderer.color(colorBits);
        renderer.vertex(x + cx, y + cy, 0);
    }
}

回答1:

What is an arc? Simply: the difference of two circles, constrained within two vectors (or a triangle).

A diagram may be helpful;

The radius of the larger red circle equals the outer radius of the arc. The radius of the smaller blue circle equals the inner radius of the arc, minus the diameter of the ball. The triangle shows the edges of the arc.

From here, just test the euclidean distance of the ball [from the center] against the radii of the circles, then find the two tangents from the origin to the ball and see if either of them fall pass through the angular measure of the arc.

EDIT: Realized I need something like this in my own project, so I decided to write it up;

    double ball_radius = //Your radius of the ball

    //the start and end angles of the arc
    double start = //i.e -PI/4;
    double end = //i.e PI/4;

    double innerRadius = //inner radius of arc
    double outerRadius = innerRadius + [width of lines, 10 in your code]

    /* Now all the fun mathsy stuff */

    boolean collides = false;

    double dx = bx - cx; //[bx, by] = ball coords
    double dy = by - cy; //[cx, cy] = center coords

    //get distance and direction to ball from center
    double dist = Math.sqrt(dx * dx + dy * dy);
    double dir = Math.atan2(dy, dx);

    //angles for tangents to ball from center
    double tangent_angle =  Math.asin(ball_radius/ dist);
    double dir0 = dir + tangent_angle;
    double dir1 = dir - tangent_angle;

    //check if distance is good
    if (dist + ball_radius> innerRadius && dist - ball_radius < outerRadius)
    {
        //check edges of ball against start and end of arc
        boolean d = dir > start && dir < end;
        boolean d0 = dir0 > start && dir0 < end;
        boolean d1 = dir1 > start && dir1 < end;

        //if both tangents are inside the angular measure
        if (d || d0 && d1)
        {
            collides = true;
        }
        //otherwise if one tangent is inside
        //We need to test the outside corners more precisely
        else if (d0 != d1)
        {
                double x0 = cx + outerRadius * Math.cos(start) - bx;
                double y0 = cy + outerRadius * Math.sin(start) - by;

                double x1 = cx + outerRadius * Math.cos(end) - bx;
                double y1 = cy + outerRadius * Math.sin(end) - by;

                /** And so on for the other 2 corners */
                /** If the arc is very thick, you will need to test against
                    the actual line segments at the ends of the arc */

                if (x0 * x0 + y0 * y0 < ball_radius * ball_radius
                    || x1 * x1 + y1 * y1 < ball_radius * ball_radius)
                    collides = true;

        }
    }

If the ball is only ever going to hit the inside of the arc, or 3-4 pixels of imprecision is okay when hitting the corners of the arc, then you can replace the entire if-condition in the above code with this, which is way more efficient (but messes up on the corners very slightly);

if (dist > innerRadius - ball_radius && dist + ball_radius < outerRadius)
{
    //if any tangent falls within the arc
    collides = ((dir0 > start && dir0 < end) || (dir1 > start && dir1 < end));
}

End result:



回答2:

arc (float x, float y, float radius, float start, float degrees

It seems that x,y is circle center, start is starting angle, degrees is sweep angle, so end angle is end = start + degrees

If your intersection point is ix, iy, then you can check signs of these cross products:

cp_1 = (ix-x)*sin(start)-(iy-y)*Cos(start) 
cp_2 = (ix-x)*sin(end)-(iy-y)*Cos(end) 

If cp1 has negative sign and cp2 is positive (for positive arc direction Degrees), then (ix,iy) intersection point is between (start,end) arc ends. (works for Abs(degrees) < Pi=180)

Delphi example generate arc from -15 to +30 degrees, and check whether rays from cx,cy to some points intersect this arc:

  function IsPointInArcLimits(cx, cy, ix, iy, StartAngle, Deg: Double): Boolean;
  var
    EndAngle, cp_1, cp_2: Double;
  begin
    EndAngle := DegToRad(StartAngle + Deg);
    StartAngle := degToRad(StartAngle);
    cp_1 := (ix - cx) * Sin(StartAngle) - (iy - cy) * Cos(StartAngle);
    cp_2 := (ix - cx) * Sin(EndAngle) - (iy - cy) * Cos(EndAngle);
    Result := (cp_1 <= 0) and (cp_2 >= 0);
  end;

var
  cx, cy, ix, iy, StartAngle, Degrees: Integer;
begin
  cx := 0;
  cy := 0;
  ix := 10;
  StartAngle := -15;
  Degrees := 45;
  for iy := -5 to 7 do
    if IsPointInArcLimits(cx, cy, ix, iy, StartAngle, Degrees) then
      Memo1.Lines.Add(Format('Yes  y=%d an=%f    arc %d+%d',
        [iy, RadToDeg(ArcTan(iy / ix)), StartAngle, Degrees]))
    else
      Memo1.Lines.Add(Format('No y=%d an=%f    arc %d+%d',
        [iy, RadToDeg(ArcTan(iy / ix)), StartAngle, Degrees]));

output:

No y=-5 an=-26.57    arc -15+45
No y=-4 an=-21.80    arc -15+45
No y=-3 an=-16.70    arc -15+45
Yes  y=-2 an=-11.31    arc -15+45
Yes  y=-1 an=-5.71    arc -15+45
Yes  y=0 an=0.00    arc -15+45
Yes  y=1 an=5.71    arc -15+45
Yes  y=2 an=11.31    arc -15+45
Yes  y=3 an=16.70    arc -15+45
Yes  y=4 an=21.80    arc -15+45
Yes  y=5 an=26.57    arc -15+45
No y=6 an=30.96    arc -15+45
No y=7 an=34.99    arc -15+45


回答3:

If it is possible for you to find the segments which make up the arc, then you could use the Intersector class to check if the circle collides with each segment. I don't know how you're making your arc so I don't know if you can get the segments or not.