Search Unity

Question Generating a 2D collider at runtime from the intersection between two 3D colliders

Discussion in 'Physics' started by SpaceJoker, Nov 28, 2023.

  1. SpaceJoker

    SpaceJoker

    Joined:
    Apr 27, 2018
    Posts:
    15
    In my project I have a 2D sprite moving around a 3D environment. I want the 2D sprite to have a 2D collider, but I also want it to interact with the non-convex 3D mesh collider of the environment (tagged "env"). I've attached a GO with a Quad convex collider, a non-kinematic Rigidbody and the following script:


    Code (CSharp):
    1.     [SerializeField]GameObject collisionHolder;
    2.     EdgeCollider2D edgeCollider;
    3.     // Start is called before the first frame update
    4.  
    5.     void OnCollisionEnter(Collision other) {
    6.         if (other.gameObject.tag == "env") {
    7.             if(collisionHolder == null) {
    8.             collisionHolder = new GameObject("test", typeof(EdgeCollider2D));
    9.             edgeCollider = collisionHolder.GetComponent<EdgeCollider2D>();
    10.             collisionHolder.transform.position = other.contacts[0].point;
    11.             }
    12.         }
    13.     }
    14.  
    15.     void OnCollisionStay (Collision other) {
    16.         if (other.gameObject.tag == "env") {
    17.             ContactPoint[] contacts = new ContactPoint[30];
    18.  
    19.             int numContacts = other.GetContacts(contacts);
    20.             List<Vector2> pointsList = new List<Vector2>();
    21.  
    22.             for (int i=0; i<numContacts; i++) {
    23.                 Vector2 temp = new Vector2(contacts[i].point.x, contacts[i].point.y);
    24.                 pointsList.Add(temp);
    25.             }
    26.             edgeCollider.SetPoints(pointsList);
    27.         }
    28.     }
    29.  
    30.  
    31.     void OnCollisionExit(Collision other) {
    32.         if (other.gameObject.tag == "env") {
    33.             Destroy(collisionHolder);
    34.         }
    35.     }
    36.  
    The idea is to generate an EdgeCollider2D at runtime passing through each points where the Quad moving around and the env collider touch. To do so I spawn a Gameobject (env) holding the EdgeCollider2D

    I've tried numerous variations on this idea and none seem to work. The contact points are loaded correctly and everything, but the EdgeCollider doesn't seem to change in the slightest. i've also tried using

    edgeCollider.points = new Vector2(contacts.point.x, contacts.point.y)

    inside the for loop but it didn't work either and kept giving me out of bounds index errors (how is the length of the EdgeCollider2D.points[] array defined?)

    Any ideas?
     
  2. Lenakeiz

    Lenakeiz

    Joined:
    Jun 11, 2015
    Posts:
    34
    Despite not throwing apparently any error there are some issues with your implementation.
    1. I am not sure it s possible to have 2d colliders against 3d, the reason being that Unity manages the separately.
    2. You cannot use a 2D Collider with a Rigibody, only RigidBody2D
    3. EdgeCollider2D is not dynamically sizable in the same way a List is, so it needs a defined size of this array before setting it.
    4. EdgeCollider2D component expects a list of points defining the edge in the local space of the GameObject the collider is attached to. So you need something like InverseTransformPoint on the detected contacts point.
    I hope this can point you towards a solution
     
    SpaceJoker and MelvMay like this.
  3. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,428
    You want 2D physics (Box2D) to interact with 3D physics (PhysX). You have to know these are two completely separate systems and therefore won't interact.

    Maybe you're asking something else but are not explaining it very well?
     
    SpaceJoker likes this.
  4. SpaceJoker

    SpaceJoker

    Joined:
    Apr 27, 2018
    Posts:
    15
    I know the two systems can't interact. Which is why I need to generate a 2D collider at runtime at the chosen Z axis, and this axis would need to match the shape of the mesh, which has its own 3D collider (at that Z coordinate)
     
  5. SpaceJoker

    SpaceJoker

    Joined:
    Apr 27, 2018
    Posts:
    15
    Point 1 and 2 were clear, point 3 I understood through trial and error, could you be more specific about 4?

    If I have a contact point Vector3, that's in world space right? And if I pass these coordinates to the Vector 2 array for the edgecollider, that one needs them in... Local?
    That could explain the issues I'm having. Although I've moved away from using a collider and contact points to generate the new one, I wanted to do it based on the intersection between a Plane and the mesh... But I still have no clue on how
    https://forum.unity.com/threads/create-an-edge-collision-where-a-plane-and-a-mesh-intersect.1522810
     
  6. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,428
    Well I hope someone can help you because you’ve just replied to me without really explaining more what you need; it’s not clear to me. The main confusion is why you’d need a 2D collider at all. None of this makes sense to me.

    I wrote 2D physics for Unity so I’m in a good position to help but first I need to be clear on what is required and why so I can help.
     
    SpaceJoker likes this.
  7. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,428
    Contact points are in world-space.

    All collider geometry is specified in local-space be it a 3D mesh, 2D Edge Collider etc.

    That said, all 2D collider geometry is local (relative to) the body it is attached to. If you have a Rigidbody2D on the same GameObject as the collider then it’s relative to that. If you don’t have a Rigidbody2D then it’s implicitly Static so is relative to the static ground body at the world-origin meaning that the local & world space are the same.
     
    SpaceJoker likes this.
  8. Lenakeiz

    Lenakeiz

    Joined:
    Jun 11, 2015
    Posts:
    34
    Point 3 referred to assign points to the collider, but I realised you used the SetPoints function so nevermind that.
    Point 4 you need to tranform your contact points in your local transform space because as @MelvMay said is collider geometry is specified in local space. So in your case the collisionHolder since is the one that gets to the EdgeCollider.

    Code (CSharp):
    1.  // Transform points from world space to local space relative to the collisionHolder
    2.         for (int i = 0; i < points.Count; i++)
    3.         {
    4.             points[i] = collisionHolder.transform.InverseTransformPoint(points[i]);
    5.        
    That being said I assumed that you have a RigidBody2d attached to the gameobject you want to create this collider, but I at this point I am also confused as to what you want to do with this new gameobject created with the 2d collider? What s the purpose of it?

    Maybe can you provide an example (from another game or a screenshot of yours) where you describe what you wouldlike to achieve?
    It may be that you are just overthinking it.
     
    SpaceJoker and MelvMay like this.
  9. SpaceJoker

    SpaceJoker

    Joined:
    Apr 27, 2018
    Posts:
    15
    I'm moving around Sprites in 3D space, at a fixed Z coordinates, using physics-based movement I wrote for the 2d colliders and rigidbody.

    I don't have a Rigidbody attached to the object with the edge-collider. It's just meant to be an holder for the collider, it's empty otherwise.
    I want to generate the 2D collider at runtime so that it follows the edges of the mesh the sprites are moving around. The mesh is generated procedurally which is why I can't shape the collider manually.

    But I've moved away from the Contacts-based GO spawning method and, as outlined in my other thread, I'd rather just generate the entirety of the collider from the intersection with a plane, but I'm not even sure if it'd be more intuitive, or easier.

    The implementation with the contacts and Edgecollider2d never seemed to work properly. The GOs correctly spawned where the Collider detector hit the 3d mesh detector (the code in OP was for a plane with its own 3D collider, that moved alongside one of the sprites, and was meant to detect when the procedural mesh and its collider were in range. Thus spawning the gameobject containing the EdgeCollider2D)

    Code (CSharp):
    1.  void OnCollisionEnter(Collision other) {
    2.         if (other.gameObject.tag == "environment") {
    3.             if(collisionHolder == null) {
    4.             collisionHolder = new GameObject("test", typeof(EdgeCollider2D));
    5.             edgeCollider = collisionHolder.GetComponent<EdgeCollider2D>();
    6.             collisionHolder.transform.position = other.contacts[0].point;
    7.             }
    8.         }
    9.     }
    But the shape of the collider wasn't updating properly. I've tried several different ways... I don't remember exactly all the implementations, but either collisionHolder would spawn and edgeCollider's shape and points were just the default ones (a straight line), OR the collider would spawn far, FAR away from the GO, with its points clearly being affected in some way, since it wasn't a straight line.
    Although without the mesh nearby I couldn't quite figure out if it was following its shape or it was just random coordinates.

    I remember in the first instance I would get index errors and I was using an EdgeCollider2D.points, the second one seemed to appear when using SetPoints() instead. So I guess there must be a way to reconcile the origin and the position of the points of the Edgecollider2D

    I'm sorry if this isn't very informative but I don't really keep a detailed history of all implementations of my code. I keep going back and forth between the game, the web and the code editor until i find a solution, If I don't manage I come here for help....

    So let me ask the question instead: How would YOU implement this? You had 2D sprites and you wanted them to interact with a procedural 3D mesh (just collisions), at a fixed height.

    I've tried giving the sprites 3d colliders and rigidbodies but the code I wrote for movement seems to break... So I'm asking if there are any alternative solutions, otherwise I need to go back to the drawing board for something that has been set in stone for a while now.

    Anyway I appreciate you giving me advice so far, despite the lack of depth in my posts.
     
  10. Lenakeiz

    Lenakeiz

    Joined:
    Jun 11, 2015
    Posts:
    34
    OkI still not understand what you will you do with this collider, but OK, let s try to solve the porblem of the collider spawning far away from the collision.

    Have you tried to use the InverseTransformPoint as I suggested in my previous post?
     
    SpaceJoker likes this.
  11. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,428
    Again, please explain what components are interacting with the EdgeCollider2D. Sprites obviously don't interact with anything so you've got a Rigidbody2D and some other Collider2D correct?

    I've got no mental image of what you're doing still, just a vague sense here. You put a lot of effort into words, maybe just post some images to clarify what is going on here. Could you show with an image or video what it needs to interact with. All we've got is the code above and nothing else.
     
    SpaceJoker likes this.
  12. SpaceJoker

    SpaceJoker

    Joined:
    Apr 27, 2018
    Posts:
    15
    Yeah the Sprite has a collider2D and a rigidbody 2D. i didn't specify that. I want this sprite to interact with the procedurally generated 3D environment, as if it had a 3D collider. Instead of rewriting the movement physics with 3D colliders, I wanted to generate a 2d collider that replicated the perimeter of the mesh at the Z axis value the sprites are moving on.

    I've mostly got it working though. I've used a variation on the implementation in OP.

    Basically there are four collisions at play:

    • The 2D collision for the sprites
    • The 3D collision for the environment
    • The 3D collision detector
    • The 2D Edge Collider that's created
    The collision detector is attached to the sprite. It's an invisible flat sphere (a 3D circle) with a Mesh collider. There's a gameobject on the scene that has the Edgecollider component (disabled on start) and a script to handle it. The detector has a reference to it.
    When the detector collides with the enivronment, it tells the edgecollider to activate, and it sets the sprite position as it adjacentStartPoint and adjacentEndPoint (properties of EdgeCollider2D).
    Then inside the detector the OnCollisionStay method calculates the contact points, creates a Vector2 List and passes it to the edge collider script as a parameter for a method inside its class, which is thus executed every frame the detector collides with the environment.
    Inside this method I simply set the points using edgeCollider2D.SetPoints().
    Inside the edge collider script FixedUpdate method I update the adjacent points positions, always accordingly to the sprite's transform.

    And that's it really, I obtain the result I wish. It works! But I have another issue.

    The Edge collider object is created as a child to the environment "chunk" (it's procedural gen using Chunks) in which the sprites for which I want the calculation are currently "residing" in. And this works fine, if I move the sprite to another chunk the edge collider object moves to that chunk as well.
    The issue is that in the process the calculation seems to get F***ed. so it works fine on the Chunk the sprites spawn in, but as soon as I move to another it stops working.

    From the inspector I can see that the adjacent points coordinates aren't the same as the sprite's coordinates. The Y coordinate is the same, but the X one always has a weird offset. So if my sprite transform is at X: 3, Y: 12, the adjacent points will read something like X: -5, Y: 12. It's weird, because even when the edgeCollider works properly, the gizmos showing the adjacent points lines (they are purple lines) look correct. They lead from the sprite to the start of the edge collision, and then from the end back to the sprite. So I'm not sure this discrepancy is the cause of the issue.

    Basically when I change Chunk, the edgecollider object moves away and while the collider itself seems to move accordingly to my sprite, the detector and its interaction with the environment mesh it's currently in, its position is... F***ed. It's in a whole other place. So i have to figure that issue out. Although you probably can't help me on this, not unless I post some code. Right now I can't, maybe I'll update the thread later.

    Still thank you very much for the support!