6
\$\begingroup\$

I have a car that needs a box collider.

If the car were 1 single mesh, I would only need to call:

        boxCol = gameObject.AddComponent<BoxCollider>();

and it would create a BoxCollider that perfectly fits my car, based on my car's mesh.

Now I have a car which consists of many different parts in the transformation hierarchy. (For example: the body of the car is the parent of the 4 doors. Every door is a separate game object, and has a doorknob child, etc.)

Now I need a script that changes the BoxCollider so that its box surrounds the whole car, including all of these parts.

I found this pose on Unity Answers, but it just doesn't get me the right collider.

Here is the code I'm using:

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))
                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;
        }
    }

}
\$\endgroup\$
3
  • 4
    \$\begingroup\$ "but it just doesnt get me the right collider" - Every time you say "this solution did not work" it's important to include in what way it didn't work. ie. how did its result differ from what you wanted? Without this information, it's very difficult for someone to discern what kind of help you need. \$\endgroup\$
    – DMGregory
    Commented Aug 29, 2016 at 13:26
  • 1
    \$\begingroup\$ there is no recursion, so it will only walk through first level children you can do GetComponentsInChildren and extend your target box based on child size \$\endgroup\$
    – Yevhen
    Commented Aug 29, 2016 at 18:46
  • \$\begingroup\$ well - i know. The Boxcollider doesnt even change for its children though \$\endgroup\$
    – OC_RaizW
    Commented Aug 30, 2016 at 7:40

4 Answers 4

4
\$\begingroup\$

I found out that the Boxcollider I get from the solution above was only expanding the bounds of all the children while leaving out the gameObject itself aswell as all other descendants, and it only delivered the right solution for position and rotation 0, and scale 1, which confused me at first.

here is the code that will surround the gameobject and every descendant: (keep in mind that you need to set the position and the rotation to 0 and the localScale to 1 and to get the right Collider. Just store the old transform variables somewhere and then set them to 0 (and scale to 1) , then change the collider (call this script), then set the transform variables back again) . Have fun!

public void addBoundsToAllChildren()
{
    if (boxCol == null)
    {
        boxCol = gameObject.GetComponent(typeof(BoxCollider)) as BoxCollider;
        if (boxCol == null)
        {
            boxCol = gameObject.AddComponent<BoxCollider>();
        }
    }
    Bounds bounds = new Bounds(Vector3.zero, Vector3.zero);
    Renderer thisRenderer = transform.GetComponent<Renderer>();
    bounds.Encapsulate(thisRenderer.bounds);
    boxCol.offset = bounds.center - transform.position;
    boxCol.size = bounds.size;

    allDescendants = gameObject.GetComponentsInChildren<Transform>();
    foreach (Transform desc in allDescendants)
    {
        Renderer childRenderer = desc.GetComponent<Renderer>();
        if (childRenderer != null)
        {
           bounds.Encapsulate(childRenderer.bounds);
        }
        boxCol.offset = bounds.center - transform.position;
        boxCol.size = bounds.size;
    }
}
\$\endgroup\$
4
  • 1
    \$\begingroup\$ It seems that now there's no BoxCollider.offset in Unity. \$\endgroup\$ Commented Aug 31, 2018 at 2:30
  • 1
    \$\begingroup\$ @user1566515 Instead of offset you can use BoxCollider.center I suppose \$\endgroup\$ Commented Jun 27, 2019 at 19:40
  • \$\begingroup\$ This approach does assume the root object has a Renderer, which may not be true. It's pretty common to have an empty root to contain the meshes, so everything from thisRenderer outside the loop should be inside code like if (transform.TryGetComponent<Renderer>(out var thisRenderer)){ } \$\endgroup\$
    – jhocking
    Commented Jun 3, 2020 at 13:41
  • \$\begingroup\$ I've been using this for a while and just had to modify it to fix a bug, here's the code now (along with links in the comments explaining the problem) gist.github.com/jhocking/e64a5abdcae9b294f02cec56e26fb14b \$\endgroup\$
    – jhocking
    Commented Feb 10, 2021 at 16:00
3
\$\begingroup\$

A modified version of @OC_RaizW answer for BoxCollider2D (only children taken into account, not self):

public void addBoundsToAllChildren()
{

    var boxCol = gameObject.GetComponent<BoxCollider2D>();
    if (boxCol == null)
    {
        boxCol = gameObject.AddComponent<BoxCollider2D>();
    }

    Bounds bounds = new Bounds(transform.position, Vector3.zero);

    var allDescendants = gameObject.GetComponentsInChildren<Transform>();
    foreach (Transform desc in allDescendants)
    {
        Renderer childRenderer = desc.GetComponent<Renderer>();
        if (childRenderer != null)
        {
            bounds.Encapsulate(childRenderer.bounds);
        }
        boxCol.offset = bounds.center - transform.position;
        boxCol.size = bounds.size;
    }
}
\$\endgroup\$
1
\$\begingroup\$

I had the same issue, hopefully, this will help somebody someday.

First, get all possible mesh renderers:
var renderers = gameObject.GetComponentsInParent<Renderer>().ToList();
renderers.AddRange(gameObject.GetComponentsInChildren<Renderer>());
Set the center of initial bounds to the position of the parent object and size to zero, and then encapsulate every mesh:
var bounds = new Bounds(transform.position, Vector3.zero);
foreach (var r in renderers)
{
    bounds.Encapsulate(r.bounds);
}
Add the collider to the parent object and set the size and center for the collider, keep in mind the assumption here is that the position of the object is 0:
var spaceCollider = gameObject.AddComponent<BoxCollider>();
var c = spaceCollider.center;
spaceCollider.center = new Vector3(c.x, c.y + bounds.size.y / 2, c.z);
spaceCollider.size = bounds.size;
spaceCollider.isTrigger = true;
\$\endgroup\$
1
  • \$\begingroup\$ Good solution, where the center is correct. Unfortunately when objects are rotated, the size does not match anymore. \$\endgroup\$ Commented Sep 9, 2023 at 8:48
-1
\$\begingroup\$

Maybe I'm misunderstanding the question, but why don't you just go into Unity and create the Box Collider and make it the right size you want? As opposed to trying to create it in code. As long as all the other components are a part of the car, it will surround them too.

\$\endgroup\$
4
  • 3
    \$\begingroup\$ This sounds more like a comment than an actual answer. In fact, your not addressing the actual question at all, other then suggesting a complete alternative. \$\endgroup\$
    – Gnemlock
    Commented Aug 31, 2016 at 3:40
  • 1
    \$\begingroup\$ Because I can't do that for custom meshes. \$\endgroup\$
    – OC_RaizW
    Commented Aug 31, 2016 at 8:03
  • \$\begingroup\$ @OC_RaizW I apologize, I didn't realize that. \$\endgroup\$ Commented Aug 31, 2016 at 12:10
  • \$\begingroup\$ If all you want is a box collider that surrounds your car, then Ethan's answer is correct, you can just do that in the inspector, no code required. \$\endgroup\$ Commented Sep 2, 2016 at 16:05

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .