Bounds.Contains() is not working at all as expected

I’ve been having issues where when the object is rotated (even with a 1,1,1 scale and no parent) Bounds.Contains() is returning true when the point isn’t in the bounds.
I thought maybe I had a bug in my code so I wrote a test script - literally 1 line - and the error persists.
Here is the full code of my test script

using UnityEngine;

public class test : MonoBehaviour {

    public bool TEST;
    public GameObject point;

    // Use this for initialization
    void Start () {
       
    }
   
    // Update is called once per frame
    void Update () {
        TEST = gameObject.GetComponent<Collider>().bounds.Contains(point.transform.position);
    }
}

And here is the result


As you can see the TEST box is checked, even though the sphere very clearly isn’t within the bounds of the cube.

Anyone know what’s going on? It’s driving me completely mad!
Thanks :slight_smile:

That’s indeed really weird…

There’s nothing wrong with your code as far as I can tell…
Have you tried testing dumb point like (0,0,0) ? Or infinitely far away points

Bounds is an AABB - axis aligned bounding box. It’s defined exclusively by the center and extents, and hold no information about the rotation of the object. For a collider, the .bounds is the smalles axis alligned box that contains the entire collider.

AABBs are used for rough spatialization of things. It’s much faster to check if two axis aligned boxes are overlapping than two general boxes.

If you want to know if something is inside of your box, you’ll have to do the math yourself. If you’re working with other colliders, you could use an OverlapBox check.

3 Likes

I have used it before and it usually works just fine even if rotated, I also tried this code and the issue persisted

using UnityEngine;

public class test : MonoBehaviour {

    public bool TEST;
    public GameObject point;

    // Use this for initialization
    void Start () {
     
    }
 
    // Update is called once per frame
    void Update () {
        TEST = gameObject.GetComponent<Collider>().bounds.Intersects(point.GetComponent<Collider>().bounds);
    }
}

I’ll try that other stuff you mentioned though

It literally says in the docs that it’s not rotated. If you used it before, you just didn’t notice the bug.

Here’s a script you can add to a GameObject to draw the current bounds for a Collider or Renderer as gizmos. Then you can see the behaviour of the bounds as you rotate the object.

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

public class DrawBoundsGizmos : MonoBehaviour {
#if UNITY_EDITOR
    public enum ColliderOrRenderer {
        Collider,
        Renderer
    }

    public enum DrawWhen {
        Always,
        Selected,
        ParentSelected
    }

    public ColliderOrRenderer type;
    public DrawWhen drawWhen;

    private static readonly Vector3 gizmosSize = Vector3.one * .1f;

    public void OnDrawGizmosSelected() {
        if (drawWhen == DrawWhen.ParentSelected) {
            DrawGizmos();
        } else if (drawWhen == DrawWhen.Selected && Selection.activeTransform == transform) {
            DrawGizmos();
        }
    }

    private void OnDrawGizmos() {
        if (drawWhen == DrawWhen.Always) {
            DrawGizmos();
        }
    }

    private void DrawGizmos() {
        try {
            Bounds B = type == ColliderOrRenderer.Renderer ? GetComponent<Renderer>().bounds : GetComponent<Collider>().bounds;
            DrawGizmosFor(B);
        }
        catch {
            // nothing to draw the bounds of!
        }
    }

    public static void DrawGizmosFor(Bounds B) {
        var xVals = new[] {
            B.max.x, B.min.x
        };
        var yVals = new[] {
            B.max.y, B.min.y
        };
        var zVals = new[] {
            B.max.z, B.min.z
        };

        for (int i = 0; i < xVals.Length; i++) {
            var x = xVals[i];
            for (int j = 0; j < yVals.Length; j++) {
                var y = yVals[j];
                for (int k = 0; k < zVals.Length; k++) {
                    var z = zVals[k];

                    var point = new Vector3(x, y, z);
                    Gizmos.DrawCube(point, gizmosSize);

                    if (i == 0) {
                        Gizmos.DrawLine(point, new Vector3(xVals[1], y, z));
                    }
                    if (j == 0) {
                        Gizmos.DrawLine(point, new Vector3(x, yVals[1], z));
                    }
                    if (k == 0) {
                        Gizmos.DrawLine(point, new Vector3(x, y, zVals[1]));
                    }

                }
            }
        }
    }
#endif
}
4 Likes

Huh, I guess you’re right - and that’s a neat little script thanks :slight_smile:

I actually just wrote my own script for checking the bounds
It requires you to do all the transformations (centre, scale etc) on the parent gameobject rather than the collider component (so the collider centre is 0,0,0 and scale is 1,1,1) but that’s fine for me and it works great.

bool ColliderContainsPoint(Transform ColliderTransform, Vector3 Point, bool Enabled)
    {
        Vector3 localPos = ColliderTransform.InverseTransformPoint(Point);
        if (Enabled && Mathf.Abs(localPos.x) < 0.5f && Mathf.Abs(localPos.y) < 0.5f && Mathf.Abs(localPos.z) < 0.5f)
            return true;
        else
            return false;
    }
2 Likes

I know this is a very old post, but I just wanted to say: Joe Patrick, lots of thanks for this awesome script, it’s a pretty simple solution but very elegant and it works great!

I know we are going back some years with this, but this just saved me a bit of work, thanks JoePatrick for sharing your findings :slight_smile:

Awesome Solution dude! thanks !

Joe Patrick is king♡

I made a few changes to this, since I had the same problem:

public bool TransformBoxContainsPoint(Transform colliderTransform, Vector3 offset, Vector3 colliderSize, Vector3 point)
{
  Vector3 localPos = colliderTransform.InverseTransformPoint(point);

  localPos -= offset;

  if (Mathf.Abs(localPos.x) < (colliderSize.x/2) && Mathf.Abs(localPos.y) < (colliderSize.y/2) && Mathf.Abs(localPos.z) < (colliderSize.z/2))  return true;
  else return false;
}

Call with:

var box = gameObject.GetComponent<BoxCollider>();
var found = TransformBoxContainsPoint(box.transform, box.center, box.size, testObject.transform.position);
2 Likes

@ge01f how would i make your little method return the objects its touching instead?

I converted this into a static that is a bit easier to use.

public static bool ContainsPoint(this BoxCollider collider, Vector3 point) {
	Vector3 localPos = collider.transform.InverseTransformPoint(point);
	localPos -= collider.center;
	Vector3 size = collider.size / 2f;
	return Mathf.Abs(localPos.x) < size.x && Mathf.Abs(localPos.y) < size.y && Mathf.Abs(localPos.z) < size.z;
}

You can use anywhere you have a collider reference:

boxCollider.ContainsPoint(position);