- Home /
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?
Answer by fherbst · Jul 11, 2010 at 11:39 AM
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 :).
Answer by numberkruncher · Feb 10, 2012 at 10:10 PM
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
Answer by dbanfield · Aug 30, 2013 at 09:42 PM
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..
Thank you so much! This is exactly what I needed!
Answer by tankorsmash · Jul 07, 2019 at 06:14 AM
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");
}
}
}
i would suggest ins$$anonymous$$d of skipping on not having a collider,...
foreach (GameObject rootGameObject in Selection.gameObjects) {
if (!(rootGameObject.GetComponent<Collider>() is BoxCollider)) {
print("skipping");
continue;
}
just add a collider:
if(rootGameObject.GetComponent<Collider>() == null)
{
rootGameObject.AddComponent<BoxCollider>();
}
Answer by nauroman · Nov 19, 2019 at 08:27 PM
[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;
}
}
Thanks! :)
Is there any chance to also take rotation and scale into consideration? $$anonymous$$ost of the solutions out there seem to have some issues when the mesh/children get rotated or scaled.