3
\$\begingroup\$

I'm trying to scale a lot of dynamically created game objects in Unity3D to the bounds of a sphere collider, based on the size of their current mesh. Each object has a different scale and mesh size. Some are bigger than the AABB of the collider, and some are smaller. Here's the script I've written so far:

private void ScaleToCollider(GameObject objectToScale, SphereCollider sphere) {
    var currentScale = objectToScale.transform.localScale;
    var currentSize = objectToScale.GetMeshHierarchyBounds().size;

    var targetSize = (sphere.radius * 2);

    var newScale = new Vector3 {
        x = targetSize * currentScale.x / currentSize.x,
        y = targetSize * currentScale.y / currentSize.y,
        z = targetSize * currentScale.z / currentSize.z
    };

    Debug.Log("{0} Current scale: {1}, targetSize: {2}, currentSize: {3}, newScale: {4}, currentScale.x: {5}, currentSize.x: {6}",
        objectToScale.name, currentScale, targetSize, currentSize, newScale, currentScale.x, currentSize.x);

    //DoorDevice_meshBase                   Current scale: (0.1, 4.0, 3.0), targetSize: 5, currentSize: (2.9, 4.0, 1.1), newScale: (0.2, 5.0, 13.4), currentScale.x: 0.125, currentSize.x: 2.869114
    //RedControlPanelForAirlock_meshBase    Current scale: (1.0, 1.0, 1.0), targetSize: 5, currentSize: (0.0, 0.3, 0.2), newScale: (147.1, 16.7, 25.0), currentScale.x: 1, currentSize.x: 0.03400017

    objectToScale.transform.localScale = newScale;
}

And the supporting extension method:

public static Bounds GetMeshHierarchyBounds(this GameObject go) {
    var bounds = new Bounds(); // Not used, but a struct needs to be instantiated

    if (go.renderer != null) {
        bounds = go.renderer.bounds;
        // Make sure the parent is included
        Debug.Log("Found parent bounds: " + bounds);
        //bounds.Encapsulate(go.renderer.bounds);
    }
    foreach (var c in go.GetComponentsInChildren<MeshRenderer>()) {
        Debug.Log("Found {0} bounds are {1}", c.name, c.bounds);
        if (bounds.size == Vector3.zero) {
            bounds = c.bounds;
        } else {
            bounds.Encapsulate(c.bounds);
        }
    }
    return bounds;
}

After the re-scale, there doesn't seem to be any consistency to the results - some objects with completely uniform scales (x,y,z) seem to resize correctly, but others don't :|

Its one of those things I've been trying to fix for so long I've lost all grasp on any of the logic :| Any help would be appreciated!

\$\endgroup\$
2
\$\begingroup\$

First idea:

Rescale each object to (1.0, 1.0, 1.0) before applying your algorithm. But then I thought that maybe the objects look good when they are non-uniformly scaled to begin with.

Second idea:

I think that the problem is that the algorithm is mixing scale and size values. If your var newScale = new Vector3(...) code only works for uniform scales of 1.0, then the currentScale.<x/y/z> parts in it could be replaced by 1.0 which means that they are effectively doing nothing.

Suggestion (which I haven't tried):

Calculating the targetSize / currentSize (for each of x,y,z) will give you the newSizeRatio for each dimension. Take the minimum of them and multiply the scale vector by it:

float newSizeRatioX = targetSize.x / currentSize.x;
float newSizeRatioY = targetSize.y / currentSize.y;
float newSizeRatioZ = targetSize.z / currentSize.z;

float minimumNewSizeRatio = Math.Min(newSizeRatioX, Math.Min(newSizeRatioY, newSizeRatioZ));

Vector3 newScale = currentScale * minimumNewSizeRatio;

I believe this will work because:

  • Multiplying all dimensions with the same ratio will probably produce a uniformly scaled object.
  • Multiplying with the minimum ratio will guaranty that the object will fit inside the circle.

I hope this works :-)

\$\endgroup\$
  • \$\begingroup\$ Hey vrinek! Thanks for your reply. I actually sorted this about 2 hours after I posted this (and forgot to come back and update it!). I will post my solution, but it's similar to your last example - everything now resizes correctly, and in addition I've applied a reduction factor too - so I can resize to 50% (opr whatever) of the sphere radius. \$\endgroup\$ – Spikeh Jun 6 '14 at 15:00
1
\$\begingroup\$

use a mesh collider instead !

i just had the same problem.

but then i just used a mesh collider instead of a sphere collider and the problem almost solved itself

just be sure to update the mesh collider shared mesh as well:

var meshInstance = Instantiate(meshes[type]);
gameObject.GetComponent<MeshFilter> ().mesh = meshInstance;
gameObject.GetComponent<MeshCollider> ().sharedMesh = meshInstance;
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.