/// <summary> /// Creates a capsule collider using the height from first 2 vertices, and then getting radius from the best fit sphere algorithm. /// </summary> /// <param name="worldVertices">List of world vertices</param> /// <param name="properties">Properties of collider</param> /// <returns></returns> public Collider CreateCapsuleCollider_BestFit(List <Vector3> worldVertices, EasyColliderProperties properties) { if (worldVertices.Count >= 3) { if (properties.Orientation == COLLIDER_ORIENTATION.ROTATED) { GameObject obj = CreateGameObjectOrientation(worldVertices, properties.AttachTo, "Rotated Capsule Collider"); if (obj != null) { properties.AttachTo = obj; } } // use local verts. List <Vector3> localVertices = ToLocalVerts(properties.AttachTo.transform, worldVertices); // height from first 2 verts selected. Vector3 v0 = localVertices[0]; Vector3 v1 = localVertices[1]; float height = Vector3.Distance(v0, v1); float dX = Mathf.Abs(v1.x - v0.x); float dY = Mathf.Abs(v1.y - v0.y); float dZ = Mathf.Abs(v1.z - v0.z); localVertices.RemoveAt(1); localVertices.RemoveAt(0); // radius from best fit sphere of the rest of the vertices. BestFitSphere bfs = CalculateBestFitSphere(localVertices); CapsuleCollider capsuleCollider = Undo.AddComponent <CapsuleCollider>(properties.AttachTo); Vector3 center = bfs.Center; if (dX > dY && dX > dZ) { capsuleCollider.direction = 0; center.x = (v1.x + v0.x) / 2; } else if (dY > dX && dY > dZ) { capsuleCollider.direction = 1; center.y = (v1.y + v0.y) / 2; } else { capsuleCollider.direction = 2; center.z = (v1.z + v0.z) / 2; } capsuleCollider.center = center; capsuleCollider.height = height; capsuleCollider.radius = bfs.Radius; SetPropertiesOnCollider(capsuleCollider, properties); return(capsuleCollider); } return(null); }
/// <summary> /// Creates a sphere collider using the best fit sphere algorithm. /// </summary> /// <param name="worldVertices">List of world space vertices</param> /// <param name="properties">Properties of collider</param> /// <returns></returns> public Collider CreateSphereCollider_BestFit(List <Vector3> worldVertices, EasyColliderProperties properties) { if (worldVertices.Count >= 2) { // Convert to local space. List <Vector3> localVertices = ToLocalVerts(properties.AttachTo.transform, worldVertices); BestFitSphere bfs = CalculateBestFitSphere(localVertices); SphereCollider sphereCollider = Undo.AddComponent <SphereCollider>(properties.AttachTo); sphereCollider.radius = bfs.Radius; sphereCollider.center = bfs.Center; SetPropertiesOnCollider(sphereCollider, properties); return(sphereCollider); } return(null); }
/// <summary> /// Creates a box collider by calculating the min and max x, y, and z. /// </summary> /// <param name="worldVertices">List of world space vertices</param> /// <param name="properties">Properties of collider</param> /// <returns></returns> public Collider CreateBoxCollider(List <Vector3> worldVertices, EasyColliderProperties properties) { if (worldVertices.Count >= 2) { if (properties.Orientation == COLLIDER_ORIENTATION.ROTATED) { if (worldVertices.Count >= 3) { GameObject obj = CreateGameObjectOrientation(worldVertices, properties.AttachTo, "Rotated Box Collider"); if (obj != null) { obj.layer = properties.Layer; properties.AttachTo = obj; } } else { Debug.LogWarning("Easy Collider Editor: Creating a Rotated Box Collider requires at least 3 points to be selected."); } } List <Vector3> localVertices = ToLocalVerts(properties.AttachTo.transform, worldVertices); float xMin, yMin, zMin = xMin = yMin = Mathf.Infinity; float xMax, yMax, zMax = xMax = yMax = -Mathf.Infinity; foreach (Vector3 vertex in localVertices) { //x min & max. xMin = (vertex.x < xMin) ? vertex.x : xMin; xMax = (vertex.x > xMax) ? vertex.x : xMax; //y min & max yMin = (vertex.y < yMin) ? vertex.y : yMin; yMax = (vertex.y > yMax) ? vertex.y : yMax; //z min & max zMin = (vertex.z < zMin) ? vertex.z : zMin; zMax = (vertex.z > zMax) ? vertex.z : zMax; } Vector3 max = new Vector3(xMax, yMax, zMax); Vector3 min = new Vector3(xMin, yMin, zMin); Vector3 size = max - min; Vector3 center = (max + min) / 2; BoxCollider boxCollider = Undo.AddComponent <BoxCollider>(properties.AttachTo); boxCollider.size = size; boxCollider.center = center; SetPropertiesOnCollider(boxCollider, properties); return(boxCollider); } return(null); }
/// <summary> /// Creates a sphere collider by calculating the min and max in x, y, and z. /// </summary> /// <param name="worldVertices">List of world space vertices</param> /// <param name="properties">Properties of collider</param> /// <returns></returns> public Collider CreateSphereCollider_MinMax(List <Vector3> worldVertices, EasyColliderProperties properties) { if (worldVertices.Count >= 2) { // use local space verts. List <Vector3> localVertices = ToLocalVerts(properties.AttachTo.transform, worldVertices); float xMin, yMin, zMin = xMin = yMin = Mathf.Infinity; float xMax, yMax, zMax = xMax = yMax = -Mathf.Infinity; for (int i = 0; i < localVertices.Count; i++) { //x min & max. xMin = (localVertices[i].x < xMin) ? localVertices[i].x : xMin; xMax = (localVertices[i].x > xMax) ? localVertices[i].x : xMax; //y min & max yMin = (localVertices[i].y < yMin) ? localVertices[i].y : yMin; yMax = (localVertices[i].y > yMax) ? localVertices[i].y : yMax; //z min & max zMin = (localVertices[i].z < zMin) ? localVertices[i].z : zMin; zMax = (localVertices[i].z > zMax) ? localVertices[i].z : zMax; } // calculate center Vector3 center = (new Vector3(xMin, yMin, zMin) + new Vector3(xMax, yMax, zMax)) / 2; SphereCollider sphereCollider = Undo.AddComponent <SphereCollider>(properties.AttachTo); sphereCollider.center = center; // calculate radius to contain all points float maxDistance = 0.0f; float distance = 0.0f; foreach (Vector3 vertex in localVertices) { distance = Vector3.Distance(vertex, center); if (distance > maxDistance) { maxDistance = distance; } } sphereCollider.radius = maxDistance; SetPropertiesOnCollider(sphereCollider, properties); return(sphereCollider); } return(null); }
/// <summary> /// Sets the collider properties isTrigger and physicMaterial. /// </summary> /// <param name="collider">Collider to set properties on</param> /// <param name="properties">Properties object with the properties you want to set</param> private void SetPropertiesOnCollider(Collider collider, EasyColliderProperties properties) { collider.isTrigger = properties.IsTrigger; collider.sharedMaterial = properties.PhysicMaterial; }
/// <summary> /// Creates a capsule collider using the Min-Max method /// </summary> /// <param name="worldVertices">List of world space vertices</param> /// <param name="properties">Properties to set on collider</param> /// <param name="method">Min-Max method to use to add radius' to height.</param> /// <returns></returns> public Collider CreateCapsuleCollider_MinMax(List <Vector3> worldVertices, EasyColliderProperties properties, CAPSULE_COLLIDER_METHOD method) { if (properties.Orientation == COLLIDER_ORIENTATION.ROTATED) { GameObject obj = CreateGameObjectOrientation(worldVertices, properties.AttachTo, "Rotated Capsule Collider"); if (obj != null) { properties.AttachTo = obj; } } List <Vector3> localVertices = ToLocalVerts(properties.AttachTo.transform, worldVertices); // calculate min and max points from vertices. Vector3 min = new Vector3(Mathf.Infinity, Mathf.Infinity, Mathf.Infinity); Vector3 max = new Vector3(-Mathf.Infinity, -Mathf.Infinity, -Mathf.Infinity); foreach (Vector3 vertex in localVertices) { // Calc minimums min.x = vertex.x < min.x ? vertex.x : min.x; min.y = vertex.y < min.y ? vertex.y : min.y; min.z = vertex.z < min.z ? vertex.z : min.z; // Calc maximums max.x = vertex.x > max.x ? vertex.x : max.x; max.y = vertex.y > max.y ? vertex.y : max.y; max.z = vertex.z > max.z ? vertex.z : max.z; } // Deltas for max-min float dX = max.x - min.x; float dY = max.y - min.y; float dZ = max.z - min.z; CapsuleCollider capsuleCollider = Undo.AddComponent <CapsuleCollider>(properties.AttachTo); // center is between min and max values. Vector3 center = (max + min) / 2; // set center. capsuleCollider.center = center; // set direction and height. if (dX > dY && dX > dZ) // direction is x { capsuleCollider.direction = 0; // height is the max difference in x. capsuleCollider.height = dX; } else if (dY > dX && dY > dZ) // direction is y { capsuleCollider.direction = 1; capsuleCollider.height = dY; } else // direction is z. { capsuleCollider.direction = 2; capsuleCollider.height = dZ; } // Calculate radius, makes sure that all vertices are within the radius. // Esentially to points on plane defined by direction axis, and find the furthest distance. float maxRadius = -Mathf.Infinity; Vector3 current = Vector3.zero; foreach (Vector3 vertex in localVertices) { current = vertex; if (capsuleCollider.direction == 0) { current.x = center.x; } else if (capsuleCollider.direction == 1) { current.y = center.y; } else if (capsuleCollider.direction == 2) { current.z = center.z; } float d = Vector3.Distance(current, center); if (d > maxRadius) { maxRadius = d; } } capsuleCollider.radius = maxRadius; // method add radius / diameter if (method == CAPSULE_COLLIDER_METHOD.MinMaxPlusRadius) { capsuleCollider.height += capsuleCollider.radius; } else if (method == CAPSULE_COLLIDER_METHOD.MinMaxPlusDiameter) { capsuleCollider.height += capsuleCollider.radius * 2; } // set properties SetPropertiesOnCollider(capsuleCollider, properties); return(capsuleCollider); }
/// <summary> /// Creates a Sphere Collider by finding the 2 points with a maximum distance between them. /// </summary> /// <param name="worldVertices">List of world space vertices</param> /// <param name="properties">Properties of collider</param> /// <returns></returns> public Collider CreateSphereCollider_Distance(List <Vector3> worldVertices, EasyColliderProperties properties) { // with how min-max works, i dont think this is needed. // Future work: instead of switching to faster algorithm // calculating the convex hull from the list of points, and then using those points is nlogn // whereas our slow algorithm is n^2, and the fast one is n. if (worldVertices.Count >= 2) { // // if calculations take to long, it switches to a faster less accurate algorithm using the mean. bool switchToFasterAlgorithm = false; double startTime = EditorApplication.timeSinceStartup; double maxTime = 0.1f; List <Vector3> localVertices = ToLocalVerts(properties.AttachTo.transform, worldVertices); Vector3 distanceVert1 = Vector3.zero; Vector3 distanceVert2 = Vector3.zero; float maxDistance = -Mathf.Infinity; float distance = 0; for (int i = 0; i < localVertices.Count; i++) { for (int j = i + 1; j < localVertices.Count; j++) { distance = Vector3.Distance(localVertices[i], localVertices[j]); if (distance > maxDistance) { maxDistance = distance; distanceVert1 = localVertices[i]; distanceVert2 = localVertices[j]; } } if (EditorApplication.timeSinceStartup - startTime > maxTime) { switchToFasterAlgorithm = true; break; } } if (switchToFasterAlgorithm) { // use a significantly faster algorithm that is less accurate for a large # of points. Vector3 mean = Vector3.zero; foreach (Vector3 vertex in localVertices) { mean += vertex; } mean = mean / localVertices.Count; foreach (Vector3 vertex in localVertices) { distance = Vector3.Distance(vertex, mean); if (distance > maxDistance) { distanceVert1 = vertex; maxDistance = distance; } } maxDistance = -Mathf.Infinity; foreach (Vector3 vertex in localVertices) { distance = Vector3.Distance(vertex, distanceVert1); if (distance > maxDistance) { maxDistance = distance; distanceVert2 = vertex; } } } Vector3 center = (distanceVert1 + distanceVert2) / 2; SphereCollider sphereCollider = Undo.AddComponent <SphereCollider>(properties.AttachTo); sphereCollider.radius = maxDistance / 2; sphereCollider.center = center; SetPropertiesOnCollider(sphereCollider, properties); return(sphereCollider); } return(null); }