Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
1
Question by turndapage · Mar 05, 2017 at 03:37 PM · movementmeshcollider2dtriangulation

Triangulating mesh from collider points

Hi, I've been writing a script to copy a section of any 2D collider with a circle collider. Basically, the player can draw a circle on the screen and it will copy whatever is inside of that circle and nothing that is outside it. I get collider points from the circle collider by looping through 360 degrees of the circumference of the circle. alt text Here is a quick summary of how it works:

  • Get the 2D Collider the circle is triggered by.

  • Get the collider type (Circle, polygon, box)

  • Loop through the points of the collider clockwise and add them to a list

  • Loop through the points in the collider and see which ones are inside the circle

  • Loop through the points in the original circle collider and see which ones are contained in the other shapes collider

  • Add both lists of points together

  • Use the triangulation script to get a shape from the points.

I will get the shape I want some of the time, but it is often formed wrong. alt text The Left shape is an attempt to copy the left end of a box collider and the right shape is a copy of the right side of the same box collider. It seems to be consistent that this will be the issue depending on which side of the box I copy from.

Getting the intersection between two circle colliders always works, but box colliders and polygon colliders give me trouble.

Some steps I have tried:

  • Reversing the order of the box collider (this makes it worse)

  • Adding the circle colliders points before the triggered colliders points (this doesn't affect anything)

  • Reversing the order of the circle collider (makes things worse)

  • Decreasing the number of points for a circle collider (no change)

  • Using mesh.Optimize() (do not see any changes)

Any Ideas on how to make this more robust? I thought of using the delauny triangulation, but I don't need any complex shapes and would like to keep this running fast.

Here is my current script:

 using UnityEngine;
 using System.Collections;
 using System;
 using System.Collections.Generic;
 using System.Linq;
 
 public class CopyCircle : MonoBehaviour {
 
     Vector2 startPoint;
     Vector2 endPoint;
     float diameter;
 
     MeshRenderer meshRenderer;
 
     List<GameObject> collidedObjects;
     GameObject copy;
 
     CircleCollider2D circleCollider;
 
     public int degreesToScan = 360; // Set to how accurate collisions should be saved.
     public float percentageToCircle = .80f;
 
     void Awake ()
     {
         meshRenderer = GetComponent<MeshRenderer>();
         meshRenderer.enabled = false;
 
         circleCollider = GetComponent<CircleCollider2D>();
         collidedObjects = new List<GameObject>();
     }
     
     // Update is called once per frame
     void Update () {
         if (Input.GetMouseButtonDown(1))
         {
             startPoint = GetMousePosition();
         }
         else if (Input.GetMouseButton(1)) // If left click is down
         {
             endPoint = GetMousePosition();
 
             Debug.DrawLine(startPoint, endPoint);
 
             transform.position = new Vector3((startPoint.x + endPoint.x) / 2, (startPoint.y + endPoint.y) / 2, -1);
             diameter = Vector2.Distance(startPoint, endPoint);
 
             transform.localScale = new Vector2(diameter, diameter);
             meshRenderer.enabled = true;
         }
         else if (Input.GetMouseButtonUp(1)) // Released
         {
             meshRenderer.enabled = false;
         }
         else if (Input.GetMouseButtonDown(0) && collidedObjects.Count > 0) // Left mouse down
         {
             if (copy)
                 Destroy(copy);
             copy = Copy();
         }
         else if (Input.GetMouseButton(0) && copy)
         {
             copy.transform.position = GetMousePosition();
         }
         else if (Input.GetMouseButtonUp(0) && copy)
         {
             for (int i = 0; i < copy.transform.childCount; i++)
             {
                 for (int o = 0; o < collidedObjects.Count; o++)
                 {
                     if (o != i && 
                         copy.transform.GetChild(o).GetComponent<Collider2D>().IsTouching(copy.transform.GetChild(i).GetComponent<Collider2D>()) &&
                         (!collidedObjects[o].GetComponent<Rigidbody2D>() || !collidedObjects[i].GetComponent<Rigidbody2D>()))
                     {
                         // connect the objects
                         copy.transform.GetChild(o).gameObject.AddComponent<FixedJoint2D>().connectedBody = copy.transform.GetChild(i).gameObject.GetComponent<Rigidbody2D>();
                     }
                 }
                 copy.transform.GetComponentsInChildren<Rigidbody2D>()[i].isKinematic = false;
                 copy.transform.GetComponentsInChildren<Collider2D>()[i].isTrigger = false;
             }
         }
     }
 
     Vector2 GetMousePosition()
     {
         return Camera.main.ScreenToWorldPoint(Input.mousePosition);
     }
 
     void OnTriggerStay2D(Collider2D col)
     {
         if (meshRenderer.enabled && col.gameObject.tag != "Copy" && !collidedObjects.Contains(col.gameObject))
         {
             collidedObjects.Add(col.gameObject);
         }
     }
 
     void OnTriggerExit2D(Collider2D col)
     {
         if(collidedObjects.Contains(col.gameObject))
         {
             collidedObjects.Remove(col.gameObject);
         }
     }
 
     GameObject Copy()
     {
         GameObject copy = new GameObject("New Copy");
 
         // Do the hard stuff here
 
         // First loop through the other objects collider to see which points are inside the copy circle
         
         for (int o = 0; o < collidedObjects.Count; o++)
         {
             List<Vector2> OthersColliderPoints = new List<Vector2>();
             GameObject collidedObject = collidedObjects[o];
 
             if (collidedObject.GetComponent<BoxCollider2D>()) // Box
                 OthersColliderPoints.AddRange(GetBoxColliderEdges(collidedObject.GetComponent<BoxCollider2D>()));
             else if (collidedObject.GetComponent<PolygonCollider2D>()) // Polygon
                 OthersColliderPoints.AddRange(GetPolygonColliderPoints(collidedObject.GetComponent<PolygonCollider2D>()));
             else // Circle
                 OthersColliderPoints.AddRange(GetCircleColliderPoints(collidedObject.GetComponent<CircleCollider2D>(), circleCollider, degreesToScan));
 
             List<Vector2> PointsToAdd = new List<Vector2>();
 
             // Loop through the other shapes points and add the ones that are inside the bounds of the circle
             for (var p = 0; p < OthersColliderPoints.Count; p++)
             {
                 if (circleCollider.OverlapPoint(OthersColliderPoints[p]))
                 {
                     PointsToAdd.Add(OthersColliderPoints[p]);
                     Debug.Log(OthersColliderPoints[p]);
                 }
             }
             
             List<Vector2> CirclesColliderPoints = new List<Vector2>();
             CirclesColliderPoints.AddRange(GetCircleColliderPoints(circleCollider, collidedObjects[o].GetComponent<Collider2D>(), degreesToScan));
 
             bool isMostlyCircle = CirclesColliderPoints.Count / degreesToScan >= percentageToCircle;
             
             PointsToAdd.AddRange(CirclesColliderPoints);
             
             for (int i = 0; i < PointsToAdd.Count; i++)
             {
                 PointsToAdd[i] -= new Vector2(transform.position.x, transform.position.y); // center the points to the object
             }
 
             Vector2[] booleanShape = new HashSet<Vector2>(PointsToAdd).ToArray();
 
             for (int i = 0; i < booleanShape.Length; i++)
             {
                 Debug.Log(booleanShape[i]);
             }
 
             Mesh msh = CreateMeshFromPoints(booleanShape);
 
             GameObject subCopy = new GameObject();
             // Add required components to the copy object
             subCopy.AddComponent<MeshRenderer>().material = collidedObjects[o].GetComponent<MeshRenderer>().material;
             subCopy.AddComponent<MeshFilter>().mesh = msh;
             subCopy.AddComponent<Rigidbody2D>().isKinematic = true;
             subCopy.GetComponent<Rigidbody2D>().useAutoMass = true;
             if (isMostlyCircle)
                 subCopy.AddComponent<CircleCollider2D>();
             else
                 subCopy.AddComponent<PolygonCollider2D>().points = booleanShape;
             subCopy.GetComponent<Collider2D>().isTrigger = true;
             subCopy.transform.SetParent(copy.transform);
             subCopy.layer = LayerMask.NameToLayer("Solid");
             subCopy.tag = "Copy";
         }
 
         copy.layer = LayerMask.NameToLayer("Solid");
         copy.tag = "Copy";
         return copy;
     }
 
     public static Vector2 PointOnCircle(float radius, float angleInDegrees, Vector2 origin)
     {
         // Convert from degrees to radians via multiplication by PI/180        
         float x = (float)(radius * Math.Cos(angleInDegrees * Math.PI / 180F)) + origin.x;
         float y = (float)(radius * Math.Sin(angleInDegrees * Math.PI / 180F)) + origin.y;
 
         return new Vector2(x, y);
     }
 
     Vector2[] GetBoxColliderEdges(BoxCollider2D col)
     {
         return new Vector2[]
         {
             col.gameObject.transform.TransformPoint(col.offset + new Vector2(col.size.x, -col.size.y) * 0.5f),
             col.gameObject.transform.TransformPoint(col.offset + new Vector2(col.size.x, col.size.y) * 0.5f),
             col.gameObject.transform.TransformPoint(col.offset + new Vector2(-col.size.x, col.size.y) * 0.5f),
             col.gameObject.transform.TransformPoint(col.offset + new Vector2(-col.size.x, -col.size.y) * 0.5f)
         };
     }
 
     // Returns a list of points of circle that are inside of collider
     List<Vector2> GetCircleColliderPoints(CircleCollider2D circleCol, Collider2D col, int degreeResolution)
     {
         List<Vector2> points = new List<Vector2>();
 
         for (var d = 0; d < degreeResolution; d++)
         {
             float degree = (360 / degreeResolution) * d;
             Vector2 pointOnCircle = PointOnCircle(circleCol.radius * circleCol.transform.lossyScale.x, degree, circleCol.transform.position);
             if (col.OverlapPoint(pointOnCircle)) // If point on circle is inside other collider
                 points.Add(pointOnCircle);
         }
 
         return points;
     }
 
     List<Vector2> GetPolygonColliderPoints(PolygonCollider2D polyCol)
     {
         List<Vector2> polyPoints = new List<Vector2>();
         for (int i = 0; i < polyCol.points.Length; i++)
         {
             polyPoints.Add(polyCol.transform.TransformPoint(polyCol.points[i]));
         }
         return polyPoints;
     }
 
     Mesh CreateMeshFromPoints(Vector2[] booleanShape)
     {
         // Use triangulator to get verticies for mesh
         Triangulator triangulator = new Triangulator(booleanShape);
         int[] indices = triangulator.Triangulate();
 
 
         // Create Vector2 Verticies
         Vector3[] verticies = new Vector3[booleanShape.Length];
         for (int i = 0; i < verticies.Length; i++)
         {
             verticies[i] = new Vector3(booleanShape[i].x, booleanShape[i].y, 0);
         }
 
         // Create a mesh
         Mesh msh = new Mesh();
         msh.vertices = verticies;
         msh.triangles = indices;
         msh.RecalculateNormals();
         msh.RecalculateBounds();
         //msh.Optimize();
         msh.name = "Copy";
         return msh;
     }
 }

capture2.png (1.8 kB)
final-shapes.png (17.0 kB)
Comment
Add comment
10 |3000 characters needed characters left characters exceeded
â–¼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image
0
Best Answer

Answer by turndapage · Mar 11, 2017 at 12:04 PM

Okay. Found a solution that worked for me.

Used this Delauny script to convert the points before using them for the polygon collider and mesh.

Here was the function I wrote to simplify it for myself:

 Vector2[] ConvertPointsToDelauny(Vector2[] points)
     {
         List<uint> colors = new List<uint>();
         float xMax = -1000;
         float xMin = 1000;
         float yMax = -1000;
         float yMin = 1000;
 
         for (int i = 0; i < points.Length; i++)
         {
             colors.Add(0);
             if (points[i].x > xMax)
                 xMax = points[i].x;
             if (points[i].x < xMin)
                 xMin = points[i].x;
             if (points[i].y > yMax)
                 yMax = points[i].y;
             if (points[i].y < yMin)
                 yMin = points[i].y;
         }
         float xSize = xMax - xMin;
         float ySize = yMax - yMin;
         Delaunay.Voronoi v = new Delaunay.Voronoi(points.ToList(), colors, new Rect(0, 0, xSize, ySize));
 
         return v.HullPointsInOrder().ToArray();
     }

I use this function before the rest of my script to re-arrange the points so the lines won't interesect.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
â–¼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

3 People are following this question.

avatar image avatar image avatar image

Related Questions

Moving an object with a collider in 2d? 1 Answer

Fast moving objects problem 2 Answers

Dynamic meshes: Triangulation methods and Alternatives 0 Answers

Triangulate a polygon 1 Answer

Weird behaviour after Collision? 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges