A script that auto generated the box collider around a 3d model

Hi there, as mention on the title i really need some help/advice on how can i go about doing it. I have a 3DS Max 2009 model that is a .fbx being import into Unity3D. I hope to create a script which can auto create the box collider that will have the model place inside the box collider after the model is being import into Unity3D.

Please help me on this. Thanks. :slight_smile:

Below is a example of what i am trying to do but throught a script automatically:

3 Answers

3

When you attach a box collider on a mesh , it is automatically scaled to fit the model. At least it did with my models.

Same here, I have an animation on a mesh, the box collider automatically adjusts to the new size on the mesh. Thumbs+

–

Well, it depends on how exactly the box collider should fit the model. If it’s sufficient when it matches the AABB of the model it’s quite easy.

Just create an AssetPostProcessor and attach a BoxCollider. I would do it like this:

using UnityEngine;
using UnityEditor;

//C#
class AddBoxColliderPostProcessor : AssetPostprocessor
{
    void OnPostprocessModel (GameObject g)
    {
        if (!assetPath.Contains("model")) // Just a simple restriction to only process models that contains the word "model" in it's path
            return;                       // without a check it will do the following with every imported asset!
        Renderer[] allRenderers = g.GetComponentsInChildren<Renderer>();
        foreach(Renderer R in allRenderers)
        {
            R.gameObject.AddComponent<BoxCollider>();
        }
    }
}

Unity automatically resizes the BoxCollider to match the bounds of the attached Renderer (if there is one). You can also set the center and size manually (but it should give you the same result):

    foreach(Renderer R in allRenderers)
    {
        BoxCollider BC = R.gameObject.AddComponent<BoxCollider>();
		BC.center = R.bounds.center;
		BC.size = R.bounds.size;
    }

I’ve tested it on a simple model and it works as expected :wink:

If you want you can do much more in the postprocessor. Adding scripts adding child objects, whatever… but keep in mind all AssetPostProcessors are executed for every imported model. You need some clever selection-condition. The easiest thing you can do is place the models that should get a BoxCollider in a seperate folder / subfolder that is called “AddBoxCollider” and check the asset-path:

if (!assetPath.Contains("AddBoxCollider"))
    return;                 

It’s not very nice to spread your assets like this since it’s much harder to find something, but it’s a way ;). Instead of using a folder you can rename your asset and include some kind of keyword in the name.

Ohh and don't forget that you have to reimport the asset ;) And if you dragged the model already into the scene you may have to press "revert" (to prefab) at the top in the inspector

–

@Giantbean: No, it's ok asking for more details on the same issue here ;). I never was in need of this myself, but afaik when you use AddComponent to add a box collider to a gameobject which already has a MeshRenderer it should resize the box automatically. If not, my second piece of code should work as well. So yes, you should be able to do this at runtime.

–

@Bunny83 I had a mesh that had the pivot off center and zeroing the bounds placed it where I needed it. This may not work for every-ones needs but so far it has done what I need perfectly. P.S. Thanks for the tip on adding spaces to the generic parameters < > in the forum post. I have edited my above post accordingly

–

I ran into the issue mentioned above with having odd sizes caused by the bounds center of zero. It seemed to be caused more by the fact that the pivot was not centered. I commented out bounds.center=Vector3.Zero and it worked fine without that line. Thanks Again Bunny83 Now I just need a way to reset the pivot to the mesh center without opening Maya again.

–

OK, I tried that and got what I needed. Thanks for the assist!

–

I assume that you want to create a box collider that can include multiple objects? Otherwise adding a box collider component to your object would already do the trick.

So in order to do so - as @Giantbean already pointed out - the optimal solution would be to use the MeshFilters to get the bounds of all child objects and unite them to one bound encapsulating the whole thing. If you simply add a box collider to the parent object which in most cases appears to have no mesh itself you will get a box that has the size Vector3(1,1,1) and is placed at the pivot of this object. Here is how you’d do it:

 using UnityEngine;
 using UnityEditor;
 
 class AddBoxColliderPostProcessor : AssetPostprocessor
{
    void OnPostprocessModel(GameObject gameObject)
    {
        MeshFilter[] childFilters = gameObject.GetComponentsInChildren<MeshFilter>();
        if (childFilters.Length != 0)
        {
            Bounds bounds = childFilters[0].sharedMesh.bounds;
            foreach (MeshFilter filter in childFilters) bounds.Encapsulate(filter.sharedMesh.bounds);
            gameObject.GetComponent<BoxCollider>().center = bounds.center;
            gameObject.GetComponent<BoxCollider>().size = bounds.size;
        }
    }
}

You could also combine the bounds of all the Renderers of your children. But that would cause problems if for whatever reason your objects are not at the very center of your scene or are rotated in world space. To correct that you would use:

gameObject.GetComponent<BoxCollider>().center = gameObject.transform.InverseTransformPoint(bounds.center);
gameObject.GetComponent<BoxCollider>().size = gameObject.transform.InverseTransformVector(bounds.size);

But this will only work if your object is rotated by 90deg steps, because the renderer will always use the worlds x-,y- and z-direction. So as mentioned in the beginning the MeshFilter is the way here.