10
votes

I'm developing a game that you drag and drop objects into boxes and I have no idea what's the best and most efficient way to detect whether my objects are in a box.

I'm well aware of colliders and I'm using BoxColliders and triggers to find out whether my object is touching a box but I want to detect the moment when my object (which we can assume to be sphere for sake of simplicity but later will be a mesh) is completely inside my box trigger/collider.

I read about "Contains" method of colliders but IIRC they just check if one single point inside that collider but I'm interested to know if the whole object is inside the collider.

Thanks in advance folks.

9

9 Answers

2
votes

Short answer: If you want 100% accuracy, your algorithm will never be better than O(|V|) (worst case) where V = {all vertices in mesh}, meaning you'd run .Collides() over every vertex and break if you find one that is outside your container.

Longer answer: Several methods exist to subdivide mesh surfaces, examples include: KD-Trees, OcTrees. These are beyond the scope of an SO answer to provide a full implementation, I suggest you see the wiki pages for details.

You could use these methods to divide your mesh up in to smaller sets of vertices. To speed up your algorithm you would start at the root of your subdivision tree and test if that node is contained in your container box. Keep working through the tree until you find a node that is not contained by your box. This will allow your "contains" test to fail faster but ultimately you'll wind up testing every vertex if your box contains your mesh.

Caveat: This solution does not work if your mesh is animated. In that case your best bet is to use the bounds around things like arms, feet, legs, etc and use that information to cull your Contains() tests. Again, you'll end up having to test every vertex if the mesh is fully inside your box.

2
votes

enter image description here

All boxes have a boxCollider. If an object touches the second box, the object must be inside the first box.

This is not a good solution but, maybe it will be usefull.

1
votes

Use Renderer.bounds property to get bounding box of your object.

Depending on what object you have and how accurate you want to check it to be inside a collider you might than use one of the simple ways to determine that.

1
votes

for a more refined and cleaner approach. this would be perfect for what you are doing . you can check the distance between the object you are dragging and the box .

the box has a x,y,z value which represents its position in space.

so when you drag your gameobject it may be just 0.2 on the x,y, or z away from the centere of your box. so just use this method to calculate the distance between your dragged object and the box.

    var other :Transform;
    function Update()
    {
        var dist = Vector3.Distance(other.position, transform.position);

    // while dragging 
    if(dist <10)// 10 being on all 3 axiz .
    {
    //dragged object.position = box position
    }

if (dist == 0)
{
print("i am in the centre of the box");
}

}

thus your gameobject will be in the box .

1
votes

The box within a box solution above is a good option, but if that won't work (due to variably sized/shaped objects) you might be able to accomplish something with Physics.Raycast or Collider.Raycast. I had a similar problem where I needed to test if arbitrary points were contained inside colliders (many of which were unusual blobby concave objects).

The basic idea is a "bed of nails" approach where I cast rays toward the point from multiple directions. If I hit the outside collider on all of the rays, then I can be pretty confident that the point is contained inside the collider (but still not completely certain). Here's a pic:

raycast contains

In this picture, we're trying to see if the blue point is inside the yellow collider. The GREEN arrows represent successful raycasts (the yellow collider is hit), and the PINK one is unsuccessful (the yellow collider is NOT hit).

Here is a code snippet illustrating this:

public static class CollisionUtils {

    private static readonly Vector3[] raycastDirections;

    // These are the directions that we shoot rays from to check the collider.
    static UltrasoundCollisionUtils() {
        raycastDirections = new Vector3[5];
        raycastDirections[0] = new Vector3(0,1,0);
        raycastDirections[1] = new Vector3(0,-1,-0);
        raycastDirections[2] = new Vector3(0,0,1);
        raycastDirections[3] = new Vector3(-1.41f, 0, -0.5f);
        raycastDirections[4] = new Vector3(1.41f, 0, -0.5f);
    }

    public static bool IsContained (Vector3 targetPoint, Collider collider) {
        // A quick check - if the bounds doesn't contain targetPoint, then it definitely can't be contained in the collider

        if (!collider.bounds.Contains(targetPoint)) {
            return false;
        }

        // The "100f * direction" is a magic number so that we 
        // start far enough from the point.
        foreach (Vector3 direction in raycastDirections) {
            Ray ray = new Ray(targetPoint - 100f * direction, direction);

            RaycastHit dummyHit = new RaycastHit();
            // dummyHit because collider.Raycast requires a RaycastHit
            if (!collider.Raycast(ray, out dummyHit, 100f)) {
                return false;
            }
        }

        return true;
    }
}

One way you could adapt this algorithm is rather than use Collider.Raycast, do a Physics.Raycast. If the rays hit anything other than your collider, then you know that your target object isn't entirely in the collider.

0
votes

Add a BoxCollider which surrounds the whole of the object you are testing for and check its bounds min and max are contained within the BoxCollider its entering ... this may not suit for complex mesh objects but you may be able to get away with it and its cheap

0
votes

Simply, You can place a box Collider at the bottom of your container.. If your sphere or whatever object touches that then it is completely inside the container..

0
votes

There is one method though....not that accurate but will work :

  1. Make four empties with collider(Circle / Box) like four wheels of a car and make the object parent to these empty objects with collider.
  2. Then assign every collider a bool whether it is touched or not....
  3. Count the number of colliders touched along with the actual mesh collider...Roughly estimates the orientation of the overlap. If it is 2 that means touched or overlaped from sideways. Ex: If two collider + the mesh collider is colliding which means it is overlapped sideways...