Auto-sizing primitive collider based on child meshes

Hi all,

I have a bunch of game objects parented to an Empty:

EmptyObject

+- GameObject

+- GameObject

+- ...

+- GameObject

Now I'd like to add a Box Collider to the parent EmptyObject and have it resized to fit all containing GameObjects.

When applying a Collider to a >>>single<<< GameObject, it is being automatically resized to fit. And rightclicking on the collider in the editor and selecting "Reset" also auto-resizes the collider to fit. Is there a way to get this funtionality when dealing with a group of objects?

I was also in need of this functionality.

The following implementation seems to do the trick for me. Simply create a folder in your game project called “Editor” and create a C# script called “ColliderToFit.cs” and paste the following source.

Note: This currently only works for BoxColliders, but it isn’t hard to add support for SphereColliders if necessary

using UnityEngine;
using UnityEditor;
using System.Collections;

public class ColliderToFit : MonoBehaviour {
	
	[MenuItem("My Tools/Collider/Fit to Children")]
	static void FitToChildren() {
		foreach (GameObject rootGameObject in Selection.gameObjects) {
			if (!(rootGameObject.collider is BoxCollider))
				continue;
			
			bool hasBounds = false;
			Bounds bounds = new Bounds(Vector3.zero, Vector3.zero);
			
			for (int i = 0; i < rootGameObject.transform.childCount; ++i) {
				Renderer childRenderer = rootGameObject.transform.GetChild(i).renderer;
				if (childRenderer != null) {
					if (hasBounds) {
						bounds.Encapsulate(childRenderer.bounds);
					}
					else {
						bounds = childRenderer.bounds;
						hasBounds = true;
					}
				}
			}
			
			BoxCollider collider = (BoxCollider)rootGameObject.collider;
			collider.center = bounds.center - rootGameObject.transform.position;
			collider.size = bounds.size;
		}
	}
	
}

@felix thanks for the tips

You can surely write a script to do that.

You can get the extents of a object by using its bounds: http://unity3d.com/support/documentation/ScriptReference/Bounds.html

Than you can write an editor script that goes recursively through all your child objects and combines all their bounds to a bigger one.

Finally you create a box collider based on the "outer bounds" you have calculated.

You can then use your code as a menu item: http://unity3d.com/support/documentation/ScriptReference/MenuItem.html

I hope that helps! If you aren't already into scripting, this will be a nice way to learn it :).

This is so awesome, thanks! Really good for my 2d platform game - I just attach this modified version of your script below to a parent empty object, then add child sprite tiles as much as I want and the parent Collider perfectly fits the group of tiles. Cool!

  private void FitColliderToChildren (GameObject parentObject)
  {
       BoxCollider bc = parentObject.GetComponent<BoxCollider>();
       if(bc==null){bc = parentObject.AddComponent<BoxCollider>()}
       Bounds bounds = new Bounds (Vector3.zero, Vector3.zero);
       bool hasBounds = false;
       Renderer[] renderers =  parentObject.GetComponentsInChildren<Renderer>();
       foreach (Renderer render in renderers) {
           if (hasBounds) {
               bounds.Encapsulate(render.bounds);
           } else {
               bounds = render.bounds;
               hasBounds = true;
          }
      }
     if (hasBounds) {
           bc.center = bounds.center - parentObject.transform.position;
           bc.size = bounds.size;
     } else {
           bc.size = bc.center = Vector3.zero;
           bc.size = Vector3.zero;
     }
  }
}

Edit: Just added that one as answer since it answers the question and made it suitable to any situation…

Here’s the same script as http://answers.unity.com/answers/216129/view.html just updated for Unity 2018.4:

 using UnityEngine;
 using UnityEditor;
 using System.Collections;
 
 public class ColliderToFit : MonoBehaviour {
     
     [MenuItem("My Tools/Collider/Fit to Children")]
     static void FitToChildren() {
         foreach (GameObject rootGameObject in Selection.gameObjects) {
             if (!(rootGameObject.GetComponent<Collider>() is BoxCollider)) {
                 print("skipping");
                 continue;
             }

             bool hasBounds = false;
             Bounds bounds = new Bounds(Vector3.zero, Vector3.zero);
             
             for (int i = 0; i < rootGameObject.transform.childCount; ++i) {
                 Renderer childRenderer = rootGameObject.transform.GetChild(i).GetComponent<Renderer>();
                 if (childRenderer != null) {
                     if (hasBounds) {
                         bounds.Encapsulate(childRenderer.bounds);
                     }
                     else {
                         bounds = childRenderer.bounds;
                         hasBounds = true;
                     }
                 }
             }
             
             BoxCollider collider = (BoxCollider)rootGameObject.GetComponent<Collider>();
             collider.center = bounds.center - rootGameObject.transform.position;
             collider.size = bounds.size;
            print("working");
         }
     }
     
 }

[RequireComponent(typeof(BoxCollider))]
public class BoxColliderToChildrenSize : MonoBehaviour
{
[ContextMenu(“Fit BoxCollider to children”)]
void FitBoxColliderToChildren()
{
var bounds = new Bounds(Vector3.zero, Vector3.zero);

        bounds = EncapsulateBounds(transform, bounds);

        var collider = GetComponent<BoxCollider>();
        collider.center = bounds.center - transform.position;
        collider.size = bounds.size;
    }

    private Bounds EncapsulateBounds(Transform transform, Bounds bounds)
    {
        var renderer = transform.GetComponent<Renderer>();
        if (renderer != null)
        {
            bounds.Encapsulate(renderer.bounds);
        }

        foreach (Transform child in transform)
        {
            bounds = EncapsulateBounds(child, bounds);
        }

        return bounds;
    }
}

The previous examples didn’t work well for me, I’m editing a prefab that also has some scaling.
I end up doing this:

#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;

[RequireComponent(typeof(BoxCollider))]
public class WappingCollider : MonoBehaviour {

#if UNITY_EDITOR

    [MenuItem("Custom/Wrap BoxCollider to children %&f")]
    static void WrapBoxColliderToChildren() {
        foreach (var parentObject in Selection.gameObjects) {
            var bounds = new Bounds(Vector3.zero, Vector3.zero);
            var hasBounds = false;
            for (int i = 0; i < parentObject.transform.childCount; ++i) {
                var childRenderer = parentObject.transform.GetChild(i).GetComponent<Renderer>();
                if (childRenderer) {
                    //print($"{childRenderer.name} bounds {childRenderer.bounds}");
                    if (hasBounds) {
                        bounds.Encapsulate(childRenderer.bounds);
                    } else {
                        bounds = childRenderer.bounds;
                        hasBounds = true;
                    }
                }
            }
            if (hasBounds) {
                var boxCollider = parentObject.GetComponent<BoxCollider>();
                if (!boxCollider) boxCollider = parentObject.AddComponent<BoxCollider>();
                var scale = parentObject.transform.localScale;
                scale = new Vector3(1 / scale.x, 1 / scale.y, 1 / scale.z);
                boxCollider.center = Vector3.Scale((bounds.center - parentObject.transform.position), scale);
                boxCollider.size = Vector3.Scale(bounds.size, scale);
                //print($"{parentObject.name} wrapped");
            }
            //else {
            //    print($"Skipping {parentObject.name}");
            //}
        }
    }

#endif
}

WORKS FOR BOTH BOX AND SPHERE COLLIDER

 /**
         Resizes the collider of gameObject to fit all meshes attached to it. Only works for Box/Shpere collider
         */
        public static void ResizeColliderToFitMesh(GameObject sourceObject)
        {
            Collider collider = sourceObject.GetComponent<Collider>();
            Bounds objectBounds = GetMeshBounds(sourceObject);

            if (collider.GetType() == typeof(SphereCollider))
            {
                var sphereCollider = (SphereCollider)collider;
                sphereCollider.center = objectBounds.center - sourceObject.transform.position;
                sphereCollider.radius = objectBounds.extents.x;
            }
            else if (collider.GetType() == typeof(BoxCollider))
            {
                var boxCollider = (BoxCollider)collider;
                boxCollider.center = objectBounds.center - sourceObject.transform.position;
                boxCollider.size = objectBounds.size;
            }
        }