- Home /
Test to see if a Vector3 (point) is within a boxcollider.
Is there a way to get the 3 rotated axis of the box collider to test if a point lies within it?
The quickest technique I know to obtain a boolean result for point within an OBB relys on axis, center, and the box's extent, However I cannot find a way to get the axis.
Does anybody know how to do this when the box collider isn't known.
In my example; I have a unknown number of box colliders in the game and I want to check if a vector3 contains any box collider?
All the examples I see are only good if you already know the box collider you're checking...
Answer by efge · Mar 23, 2011 at 07:58 PM
Maybe you could use Bounds.Contains on Collider.bounds.
Hmmmm . . I am trying to do something similar, but when I use Bounds.Contains where the bounds comes from a rotated box collider it lies about the result. . . . to my face. The documentation specifically says bounds is AABB only. So i'm not sure that is working for you. If anyone knows of a way to tell if a point is inside a rotated box collider, I'd be very very interested.
I looked at this, but thought it wasn't what I wanted. I confused the size attribute as a float, but it's actually a Vector3. Just FYI in case anyone makes the same mistake I did.
Bounds don't have rotation. This won't give you a Bounds fitting exactly the rotated BoxCollider, but ins$$anonymous$$d makes a new axis-aligned Bounds object in which the rotated BoxCollider is fully contained - so it will be bigger than the BoxCollider.
You might consider accepting the answer by @$$anonymous$$ikeEnoch ins$$anonymous$$d. That's how I'd do it too.
edit: Argh, didn't check how old this was, just saw that it had a new comment.
Answer by MikeEnoch · Nov 25, 2015 at 12:19 PM
Also a late reply, but this question is high on the Google search results for OABB checks with BoxColliders, so worth mentioning that something like this would probably be faster than a raycast:
bool PointInOABB (Vector3 point, BoxCollider box )
{
point = box.transform.InverseTransformPoint( point ) - box.center;
float halfX = (box.size.x * 0.5f);
float halfY = (box.size.y * 0.5f);
float halfZ = (box.size.z * 0.5f);
if( point.x < halfX && point.x > -halfX &&
point.y < halfY && point.y > -halfY &&
point.z < halfZ && point.z > -halfZ )
return true;
else
return false;
}
Works by transforming your point into the box's local space and then checking against the box dimensions directly.
This is a great solution.
As an aside, the 6 '&&' checks can be optimized by checking 'II' (and flipping the signs) to benefit from run-time short circuit evaluation.
It can already benefit from run-time short circuit evaluation, because as soon as one of the and conditions fail, the whole thing does
Damn, you're absolutely right, I stand corrected.
This is an awesome solution. I've been banging my head against my keyboard for hours trying to figure out why my ** bounds check wouldn't work. This solves everything nicely. Thank you very much.
Answer by pjcarey97 · Sep 03, 2014 at 09:53 PM
This was three and a half years ago, but useful tip for others who come across this question: put the box in it's own layer, cast a ray from the point your checking to the center of the box, and make sure your layermask is only that layer. If your raycast returns false, the point IS inside the box, if it returns true, your point IS NOT inside the box. Then if you still care what layer the box is in, return it to the layer it was in.
Answer by jhughes2112 · Feb 23, 2018 at 08:00 AM
So, I know this is an ancient thread, but I found a neat way to do the same thing without all the branches, as min and max are often implemented without comparisons. It's pretty much doing the same thing as above, just differently.
static bool IsInsideBounds(Vector3 worldPos, BoxCollider bc)
{
Vector3 localPos = box.transform.InverseTransformPoint(worldPos);
Vector3 delta = localPos - bc.center + bc.size * 0.5f;
return Vector3.Max(Vector3.zero, delta)==Vector3.Min(delta, bc.size);
}
Even it's a bit shorter than what $$anonymous$$ike posted it's certainly not better or faster. Vector3.$$anonymous$$in and $$anonymous$$ax are implemented like this:
public static Vector3 $$anonymous$$in(Vector3 lhs, Vector3 rhs)
{
return new Vector3($$anonymous$$athf.$$anonymous$$in(lhs.x, rhs.x), $$anonymous$$athf.$$anonymous$$in(lhs.y, rhs.y), $$anonymous$$athf.$$anonymous$$in(lhs.z, rhs.z));
}
And $$anonymous$$athf.$$anonymous$$in / $$anonymous$$ax is implemented like this:
public static float $$anonymous$$in(float a, float b)
{
return (a >= b) ? b : a;
}
So you get a lot additional method calls and no benefit of early exit at all. You will always have to go though all conditions. $$anonymous$$ethod calls can have a huge impact on performance if you use such a method literally 100k times a frame. Have a look at this bilinear texture resampling algorithm i've posted over here. The "inlined" and optimised version runs about 8 times faster than the one using the straight forward solution. The optimised version is the one on the dropbox.
So I would always prefer $$anonymous$$ike's solution over this one. Btw: How would you implement a $$anonymous$$ / max function without comparison? You get two inputs and have to return the smaller / greater one. How would you do that without a comparison?
$$anonymous$$ikes solution could be written a bit more compact:
bool PointInOABB (Vector3 point, BoxCollider box )
{
point = box.transform.InverseTransformPoint( point ) - box.center;
var half = box.size * 0.5f;
if( point.x < half.x && point.x > -half.x &&
point.y < half.y && point.y > -half.y &&
point.z < half.z && point.z > -half.z )
return true;
return false;
}
Or a bit more efficient:
bool PointInOABB (Vector3 point, BoxCollider box )
{
point = box.transform.InverseTransformPoint( point );
var c = box.center;
var s = box.size;
float X = s.x * 0.5f + point.x - c.x;
if (X < 0 || X > s.x)
return false;
float Y = s.y * 0.5f + point.y - c.y;
if (Y < 0 || Y > s.y)
return false;
float Z = s.z * 0.5f + point.z - c.z;
if (Z < 0 || Z > s.z)
return false;
return true;
}
This avoids unnecessary method calls and unnecessary calculations in case of an early exit. Note that depending on the type of game it might be beneficial to re-order the x,y and z cases to better match the situation. For example for some kind of RTS game most things will be on the same height (y value). It's much more common to have different x and z values so those should be checked first and y last. However it depends on the general layout.
Thanks for the detailed response. I think there's some parts in it that I agree with, and some that are more nuanced than you suggest. Hear me out.
You're absolutely right that in naive, unoptimized C#, function call overhead is expensive. You can measure that in the editor directly. When I did that, my function was about 50% slower than the most optimized version you provided above. Specifically, a million iterations of calls to a basic function (with a tiny bit of other overhead) was about 7.5 seconds versus 11.5 seconds. I was a little surprised it mattered that much, and was ready to call it there and eat some crow and pat you on the back. After all, that circumstance, you definitely are correct.
Then I decided to produce a build and run the same test, given the JIT in C# might be disabled in the Editor, or in some other way interfere with real runtime behavior. That showed your version running the same code in 6.3 seconds, and $$anonymous$$e ran in 3.9 seconds. Almost 40% faster. I was happily surprised, again, that optimized C# could get as much as a typical C++ compiler would.
So... it depends. If your platform has no JIT and you aren't using IL2CPP, I suspect you might have better performance by doing all the manual removal of function calls as you said... but I would probably check to see if AOT compilation is smart enough to inline functions that naive C# does not before making that statement. If you are using any IL2CPP or JIT platforms, function call overhead melts away with inlining of functions and the branch misprediction matter more.
Regardless, performance tuning is complicated and things that work in one environment often do not translate to another. The answer? Both solutions are better sometimes. I just liked how short and simple $$anonymous$$e was, and felt I'd share it. Now I've learned a little more about C# in the process. Thanks!
Btw: How would you implement a $$anonymous$$ / max function without comparison? You get two inputs and have to return the smaller / greater one. How would you do that without a comparison?
Sorry, I meant to mention that $$anonymous$$/max for integers is easily done without a comparison. I misremembered there being float versions as well, but can't find them at the moment. For some fun reading, check out Bit Twiddling Hacks
Answer by AnKOu · Jan 16, 2019 at 11:50 PM
Nice method. Very useful.
But this kind of method definitively should be in a static util class.
And why this if / else statement ?
So as an instance method :
/// <summary>
/// Return true if the point is inside the given BoxCollider.
/// </summary>
/// <param name="p_Point"></param>
/// <param name="p_Box"></param>
/// <returns></returns>
public static bool IsInside(this Vector3 p_Point, BoxCollider p_Box)
{
p_Point = p_Box.transform.InverseTransformPoint(p_Point) - p_Box.center;
float l_HalfX = (p_Box.size.x * 0.5f);
float l_HalfY = (p_Box.size.y * 0.5f);
float l_HalfZ = (p_Box.size.z * 0.5f);
return (p_Point.x < l_HalfX && p_Point.x > -l_HalfX &&
p_Point.y < l_HalfY && p_Point.y > -l_HalfY &&
p_Point.z < l_HalfZ && p_Point.z > -l_HalfZ);
}
Same with SphereCollider
/// <summary>
/// Return true if the point is inside the given SphereCollider.
/// </summary>
/// <param name="p_Point"></param>
/// <param name="p_Box"></param>
/// <returns></returns>
public static bool IsInside(this Vector3 p_Point, SphereCollider p_Sphere)
{
p_Point = p_Sphere.transform.InverseTransformPoint(p_Point) - p_Sphere.center;
return p_Point.sqrMagnitude <= p_Sphere.radius * p_Sphere.radius;
}
And maybe when all type all colliders are supported, do a method like that :
/// <summary>
/// Is the Point inside the given Collider ?
/// Must be a convex collider.
/// </summary>
/// <param name="p_Point"></param>
/// <param name="p_Collider"></param>
/// <returns></returns>
public static bool IsInside(this Vector3 p_Point, Collider p_Collider)
{
SphereCollider l_SphereCollider = p_Collider as SphereCollider;
if (l_SphereCollider != null)
{
return p_Point.IsInside(l_SphereCollider);
}
BoxCollider l_BoxCollider = p_Collider as BoxCollider;
if (l_BoxCollider != null)
{
return p_Point.IsInside(l_BoxCollider);
}
//etc...
}
Your answer
Follow this Question
Related Questions
Quickly moving cubes fall through quad floor 3 Answers
Bump when crossing box colliders 1 Answer
Why is it so fast to move objects with collider collisions? 1 Answer
Apply Gravity to Car Physics 0 Answers