- Home /
Bounds Finding Box
I am trying to get the bounds of a weirdly shaped thing with crazy meshness. I get it that it is not as easy as gameObject.bounds. I figured the easiest way (based on the inconclusive results in my research) is to put a box such as a collider or cube around each gameObject, and just grab the bounds of that. My question is, what object should I use? Is there an object out there that is already intended for this? Or do I need to make a cube and make it translucent and such?
Final Question:
What do I wrap around a GameObject in order to get the bounds of that GameObject via the wrapper?
Answer by robertbu · Aug 25, 2014 at 01:23 AM
I'm assuming you want an AABB (Axis Aligned Bounting Box) around all of the objects. If you need the best possible bounds:
Take the mesh.bounds from each object.
Calculate the eight corners
For each corner, convert it from local to world space using Transform.TransformPoint().
Find the minimum and maximum x,y, and z. Coordinate.
Repeat with each game object using the same x, y, and z minimums and maximums.
At the end you will have the minimum and maximum values of a box around all of your objects. If you need it, it would be easy to construct a bounds from that data.
Here are two functions. GetBounds() creates an axes aligned bounding box around an array of game objects. GetBoundsPointsNoAlloc() takes a game object and a Vector3[8], and fill the array with the mesh bounding box in world space. These points are not axes aligned. They are both static functions, so if you have a Utility class where you keep static functions, you can toss them in there:
using UnityEngine;
using System.Collections;
public static class Utils {
// Gets an axis aligned bound box around an array of game objects
public static Bounds GetBounds(GameObject[] objs) {
if (objs == null || objs.Length == 0) {
return new Bounds(Vector3.zero, Vector3.zero);
}
float minX = Mathf.Infinity;
float maxX = -Mathf.Infinity;
float minY = Mathf.Infinity;
float maxY = -Mathf.Infinity;
float minZ = Mathf.Infinity;
float maxZ = -Mathf.Infinity;
Vector3[] points = new Vector3[8];
foreach (GameObject go in objs) {
GetBoundsPointsNoAlloc(go, points);
foreach (Vector3 v in points) {
if (v.x < minX) minX = v.x;
if (v.x > maxX) maxX = v.x;
if (v.y < minY) minY = v.y;
if (v.y > maxY) maxY = v.y;
if (v.z < minZ) minZ = v.z;
if (v.z > maxZ) maxZ = v.z;
}
}
float sizeX = maxX - minX;
float sizeY = maxY - minY;
float sizeZ = maxZ - minZ;
Vector3 center = new Vector3(minX + sizeX / 2.0f, minY + sizeY / 2.0f, minZ + sizeZ / 2.0f);
return new Bounds(center, new Vector3(sizeX, sizeY, sizeZ));
}
// Pass in a game object and a Vector3[8], and the corners of the mesh.bounds in
// in world space are returned in the passed array;
public static void GetBoundsPointsNoAlloc(GameObject go, Vector3[] points) {
if (points == null || points.Length < 8) {
Debug.Log ("Bad Array");
return;
}
MeshFilter mf = go.GetComponent<MeshFilter>();
if (mf == null) {
Debug.Log ("No MeshFilter on object");
for (int i = 0; i < points.Length; i++)
points[i] = go.transform.position;
return;
}
Transform tr = go.transform;
Vector3 v3Center = mf.mesh.bounds.center;
Vector3 v3ext = mf.mesh.bounds.extents;
points[0] = tr.TransformPoint(new Vector3(v3Center.x - v3ext.x, v3Center.y + v3ext.y, v3Center.z - v3ext.z)); // Front top left corner
points[1] = tr.TransformPoint(new Vector3(v3Center.x + v3ext.x, v3Center.y + v3ext.y, v3Center.z - v3ext.z)); // Front top right corner
points[2] = tr.TransformPoint(new Vector3(v3Center.x - v3ext.x, v3Center.y - v3ext.y, v3Center.z - v3ext.z)); // Front bottom left corner
points[3] = tr.TransformPoint(new Vector3(v3Center.x + v3ext.x, v3Center.y - v3ext.y, v3Center.z - v3ext.z)); // Front bottom right corner
points[4] = tr.TransformPoint(new Vector3(v3Center.x - v3ext.x, v3Center.y + v3ext.y, v3Center.z + v3ext.z)); // Back top left corner
points[5] = tr.TransformPoint(new Vector3(v3Center.x + v3ext.x, v3Center.y + v3ext.y, v3Center.z + v3ext.z)); // Back top right corner
points[6] = tr.TransformPoint(new Vector3(v3Center.x - v3ext.x, v3Center.y - v3ext.y, v3Center.z + v3ext.z)); // Back bottom left corner
points[7] = tr.TransformPoint(new Vector3(v3Center.x + v3ext.x, v3Center.y - v3ext.y, v3Center.z + v3ext.z)); // Back bottom right corner
}
}
And here is a class that I used to test the code. If you want to see the bounding box in Game view, you will need Gizmos turned on in the upper right corner of the Game window:
using UnityEngine;
using System.Collections;
public class Testing : MonoBehaviour {
Bounds bounds;
void Update() {
bounds = Utils.GetBounds (GameObject.FindGameObjectsWithTag("Enemy"));
}
void OnDrawGizmos() {
Gizmos.DrawWireCube (bounds.center, bounds.size);
}
}
@robertbu By any chance can you write some generic code? This question has been asked on this site (in different words) half a dozen times without a clear answer.
This is the best answer I have seen yet, but it still requires a lot of knowledge that people who ask this question do not have.
This is what I came up with for trying to deter$$anonymous$$e height via your much appreciated help, :-) if you wouldn't $$anonymous$$d continuing to help me:
float GetHeight(GameObject thing)
{
Bounds bounds = thing.GetComponent<$$anonymous$$eshRenderer>().bounds;
Vector3 topLeft = new Vector3(bounds.$$anonymous$$.x, bounds.max.y, bounds.$$anonymous$$.z);
Vector3 bottomLeft = new Vector3(bounds.$$anonymous$$.x, bounds.$$anonymous$$.y, bounds.$$anonymous$$.z);
topLeft = thing.transform.TransformPoint(topLeft);
bottomLeft = thing.transform.TransformPoint(bottomLeft);
return topLeft.y - bottomLeft.y;
}
(obviously, my code does not work.)
thank you for that wonderful code! i am still getting too small of a bounding box, so my error must be elsewhere. I'll just stick a cube around my object for now. I greatly appreciate your help!!
I tested the code with a variety of object and positions before I posted it. It always produced a precise bounds to the objects. You have to take a look at the result along the world axes in Scene view to verify since any perspective makes it difficult to tell if all the object are inside the bounds. If you need a bit of space in addition to the exact bounds, you can use Bounds.Expand() to expand the bounds after it is returned.
@robertbu i think the problem lays with the polygon tool i am using from the asset store. your code works absolutely amazingly with everything except for those polygons! (the $$anonymous$$ and max y are both the same)