Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice
  4. Dismiss Notice

Question is it possible to stop holes in Composite Collider 2d?

Discussion in '2D' started by enhawk, Jun 5, 2021.

  1. enhawk

    enhawk

    Joined:
    Aug 22, 2013
    Posts:
    832
    fill-holes.png I'm using a bunch of randomly placed colliders under a composite collider 2D, it works for the most part but occasionally I have a few gaps inside the composition which become holes.

    Is there a way to close these gaps automatically? Make the Composite Collider fully solid?
     
    Last edited: Jun 5, 2021
  2. enhawk

    enhawk

    Joined:
    Aug 22, 2013
    Posts:
    832
    Anyone have an idea how to brute force search for this holes? Right now the only option I can think of is scanning the entire area and adding patches.

    Would be cool if there was a simple checkbox "convex shape" or something...?
     
  3. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,546
    I don't understand why there would be a hole there if the original colliders covered that area. If that's the case then it's a bug. There's no feature to "close gaps" because they shouldn't be produced in the first place.

    The way that could happen would be a bug in the 3rd party tessellation library we use but it's proven to be very very stable and reliable over the years.

    If you can reproduce this easily in a simple project then I would ask that you submit a bug report for me to look at and provide me the case number. Either that or host the project file or send me a DM with your email and I'll set-up a place to host it for you.

    Thanks.
     
  4. enhawk

    enhawk

    Joined:
    Aug 22, 2013
    Posts:
    832
    Thanks for the reply,

    The original colliders are not covering the gap in the example above on the left. On the right is the desired result. If having internal gaps in the resulting structure isn't by design, I can send over a bug report for sure!
     
  5. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,546
    Just checking you meant what you typed. You're saying the colliders on the left don't cover that little area you're indicating? The left/right images seem to indicate three pentagons that do. Anyway, if so then it's not a bug and is doing what you asked it to do.

    Beyond that, there's no specific feature to manipulate areas of it or "fill holes" but there are a few options:

    1. Add a CircleCollider2D to cover the area. This is obviously only good if you know where the holes are.

    2. Use CompositeCollider2D.GetPath and read the 1st path. This should be the concave hull. Unfortunately you cannot set this path only or delete other paths in the composite but you could use this on a PolygonCollider2D which should give you what you want. Unfortunately this creates an odd workflow.

    3. If you're doing this with PolygonCollider2D then you don't need to use the CompositeCollider2D. You can read the outline paths of all the polygon colliders (just a list of points), add them together (they can be in any order) then use a simple algorithm to calculate the concave hull and use that single path in a single PolygonCollider2D. This will give you exactly what you need. There are lots of free libraries online that will take an arbitrary set of points and produce such a hull. Here's one of the many: https://github.com/Liagson/ConcaveHullGenerator
     
    enhawk likes this.
  6. enhawk

    enhawk

    Joined:
    Aug 22, 2013
    Posts:
    832
    Thanks for the great reply, if theres no concave hull setting in composite collider, could I suggest this as a feature?

    I'm throwing a bunch of polygon shapes down randomly and parenting them to a composite collider, this is then in turn used to define a few things (sprite mask, sprite shape points) - is it safe to assume that the first path in composite collider is always the outline and not holes? If so, I could do as you suggest and try to make a polygon collider from the composite collider output. My entire game is a strange workflow so no problems there :D Thanks again for the detailed suggestions.
     
  7. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,546
    Well not really because it's about merging colliders, not producing a hull around them. That does seem a little specialized TBH but for all I know it might be a common requirement.

    Well in theory yes but this isn't true if you have more than one "island" of geometry. For instance, two separate regions completely disconnected won't have this.

    I'll do a quick experiment for you and see what might be the easiest way to calculate this given a set of exterior paths (1st path in a PolygonCollider2D). Might as well, I've got some time to spare. As Arnold would say, I'll be back...
     
  8. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,546
    Okay so I got distracted today but I finally managed to find some time.

    Here's the link to an example project: ConcaveComposite2D

    I chose to use the excellent Clipper (C# version) which is also used by the CompositeCollider2D to merge paths of various colliders. We then use Libtess2 to decompose that into edges or polygons.

    In the example above, I turn three PolygonCollider2D (which leave a space between them) into paths which are then merged using Clipper. I then take the exterior path of that union and pass it into an output PolygonCollider2D.

    This is it working: https://gyazo.com/e6b59727144ae1ea72362faa4304dd6d

    Note that you if you pass all the paths from Clipper to the output PolygonCollider2D it'll give you identical results as the CompositeCollider2D so as you can see, it's not magic, it's quite simple to do what the real composite does.

    Hope this helps.
     
    hugme12138 likes this.
  9. hugme12138

    hugme12138

    Joined:
    Aug 16, 2021
    Posts:
    2
    Hi, thank you very much for revealing the technical details! I'm trying to import CompositeCollider2D data into my fixed point version of the Box2D physics engine, I'm using LibTess.Net for tessellation, but it seems the output is all triangles. In Unity, however, the output is a much smaller number of convex polygons. Can you teach me the details of this?

    Edit:
    OK I checked source and found this
    Code (CSharp):
    1.  
    2. // Assume that the input data is triangles now.
    3. // Try to merge as many polygons as possible
    4. if (polySize > 3)
    5. {
    6.         _mesh.MergeConvexFaces(_pool, polySize);
    7. }
    8.  
    Then this is not a problem, I just need to check how to get the data out.
    Leave some code for Google friends later: just compare
    Tess.Undef


    Code (CSharp):
    1.  
    2. int maxPolySize = 8;
    3.  
    4. tess.Tessellate(WindingRule.EvenOdd, ElementType.Polygons, maxPolySize);
    5.  
    6. BodyDef obstatleBodyDef = new BodyDef()
    7. {
    8.     BodyType = BodyType.StaticBody,
    9.     Position = new FPVector2(0, 0)
    10. };
    11. var obstatleBody = world.CreateBody(obstatleBodyDef);
    12. ObstatleBodyList.Add(obstatleBody);
    13.  
    14. int numTriangles = tess.ElementCount;
    15.  
    16. FPVector2[] fpoints = new FPVector2[maxPolySize];
    17.  
    18. // Convert to FPVector2
    19. for (int i = 0; i < numTriangles; i++)
    20. {
    21.     int usedSize = 0;
    22.     for (int k = 0; k < maxPolySize; k++)
    23.     {
    24.         var p = tess.Elements[i * maxPolySize + k];
    25.  
    26.         // COMPARE THIS
    27.         if (p != Tess.Undef)
    28.         {
    29.             var vk = tess.Vertices[p].Position;
    30.             fpoints[k] = new FPVector2(vk.X, vk.Y);
    31.             usedSize++;
    32.         }
    33.     }
    34.  
    35.     try
    36.     {
    37.         // Pass usedSide into Box2D
    38.         PhysicsDef.ObstacleShape.Set(fpoints, usedSize);
    39.     }
    40.     catch (InvalidOperationException e)
    41.     {
    42.         // https://github.com/memononen/libtess2/issues/31
    43.         Debug.LogWarning($"跳过共线三角面:{i}, usedSize={usedSize}");
    44.     }
    45.     obstatleBody.CreateFixture(PhysicsDef.ObstacleShapeDef);
    46. }
    47.  
     
    Last edited: Feb 1, 2023