- Home /
Test if point is in Collider/Trigger
Is it possible to test if a point is inside of a Collider/Trigger?
I try to test if 4 points are inside of a cylinder, so I can't use the Bounds.Contains()
btw. I want to use different shapes (mesh colliders) later, so a simple script like testing the distance between point and cylinder middlepoint won't work. :/
EDIT: I was able to avoid the problem, by using a "Point in Polygon" - script (I only need to check it in 2d), but it would still be really helpful if somebody could say me how to test if a point is inside a collider/trigger!
Answer by MirrorIrorriM · Feb 05, 2013 at 06:14 PM
Although this question is old, I thought I would post another way to solve it. While DrakharStudio's solution would work for most convex shapes, many concave shapes could read inside when they actually aren't.
Take a look at this picture for example.
Because the shape is concave, it is possible for you to pass through the skin of the object, only to exit it at a later time and re-enter it. Therefor, to be truly accurate, you must also test to exit the collider. You can do this with a simple 2 directional loop.
using UnityEngine;
using System.Collections;
public class CollisionTest : MonoBehaviour {
void Update() {
Vector3 Point;
Vector3 Start = new Vector3(0,100,0); // This is defined to be some arbitrary point far away from the collider.
Vector3 Goal = transform.position; // This is the point we want to determine whether or not is inside or outside the collider.
Vector3 Direction = Goal-Start; // This is the direction from start to goal.
Direction.Normalize();
int Itterations = 0; // If we know how many times the raycast has hit faces on its way to the target and back, we can tell through logic whether or not it is inside.
Point = Start;
while(Point != Goal) // Try to reach the point starting from the far off point. This will pass through faces to reach its objective.
{
RaycastHit hit;
if( Physics.Linecast(Point, Goal, out hit)) // Progressively move the point forward, stopping everytime we see a new plane in the way.
{
Itterations ++;
Point = hit.point + (Direction/100.0f); // Move the Point to hit.point and push it forward just a touch to move it through the skin of the mesh (if you don't push it, it will read that same point indefinately).
}
else
{
Point = Goal; // If there is no obstruction to our goal, then we can reach it in one step.
}
}
while(Point != Start) // Try to return to where we came from, this will make sure we see all the back faces too.
{
RaycastHit hit;
if( Physics.Linecast(Point, Start, out hit))
{
Itterations ++;
Point = hit.point + (-Direction/100.0f);
}
else
{
Point = Start;
}
}
if(Itterations % 2 == 0)
{
print("Point is Outside");
}
if(Itterations % 2 == 1)
{
print("Point is Inside");
}
}
}
We can tell if the object is inside or outside based on the number of times it has collided with a mesh. Since you cannot see the skin of a mesh coming from inside it, we also need to project raycasts coming from the point of interest. This technique works for all objects which are not 2-Dimensional, no matter how complex the shape or where the center is.
Loved the picture! This is precisely what I had envisioned when I adapted Drakhar's script, but I couldn't be as sure as you are this solution would work 100% - even thought it did on all my tests. I'll post it as yet another answer (because I hate posting code inside comments, making them way too big) and here I ask you to please validate it with your idea. I think what leads it to be done with fewer lines of code make it more elegant and eventually easier to understand. :-P
Btw, it might also work with RaycastAll. That way you just have to cast one in each direction, but i never tried that. $$anonymous$$aybe Unity ignores additional collisions with the same collider?! If not that would simplify the function ;)
Note: This approach has the problem that it is "relative" to your starting point. You have to know that you're starting outside or all results are inverted.
RaycastAll can only detect one collision per collider. That is why I had to do the while loop to go through all the intersects with a mesh. It is really unfortunate too. $$anonymous$$akes a lot of intersect logic really annoying.
Yeah, I had it wrong. But I still think you could, at least, use $$anonymous$$athf.Infinity
for distance and Physics.Raycast
to begin with, rather than setting an arbitrary "far point".
Answer by DrakharStudio · Sep 17, 2012 at 08:44 AM
I would do something similar to the solution by SilverTabby, but casting a ray from the point of interest to the center of the collider.
static public bool IsInside ( Collider test, Vector3 point)
{
Vector3 center;
Vector3 direction;
Ray ray;
RaycastHit hitInfo;
bool hit;
// Use collider bounds to get the center of the collider. May be inaccurate
// for some colliders (i.e. MeshCollider with a 'plane' mesh)
center = test.bounds.center;
// Cast a ray from point to center
direction = center - point;
ray = new Ray(point, direction);
hit = test.Raycast(ray, out hitInfo, direction.magnitude);
// If we hit the collider, point is outside. So we return !hit
return !hit;
}
Well, that would probably work! Unfortunately I don't need it any more... this question is over a year old and I have already switched to a completely different system.
Yeah, but we found the question because we had the same need, and when we solved it, we posted it. And posting year is not shown, just day and month. ???
Yeah, it's a bit strange... anyway, thanks for the answer! This method might be handy later in the development.
$$anonymous$$ouse over on top the time and it will show the year. And the line about `test.Raycast` has a typo. It should be `out hitInfo`. Great script by the way!
Thanks Cawas for the mouse-over tip and for detecting the typo ;)
Answer by chriscode · Jul 11, 2018 at 10:33 PM
bool IsInside(Collider c, Vector3 point)
{
Vector3 closest = c.ClosestPoint(point);
// Because closest=point if inside - not clear from docs I feel
return closest == point;
}
Extremely simple and effective. I don't know if there's a performance hit from closestPoint but this is the cleanest answer.
Just heads up for people who end up here that closestPoint doesn't work on concave meshColliders.
Answer by SilverTabby · Sep 06, 2011 at 01:30 PM
The first solution that I can come up with is that if you have a Vector3 point, and a refrence to the collider that you are checking, I would do a collider.Raycast (different from Physics.Raycast!) with a ray so small, that it could only hit the collider if your point was already touching it.
The only reason I haven't already recommended collider.ClosestPointOnBounds is because I don't know if a point inside the collider will return the input point or if it will return a something on the edge of the bounding box.
var other : Collider;
var point : Vector3;
var dist : float = 0.001;
private var r : Ray = new Ray(point, Vector3.one);
private var hit : RaycastHit;
if(other.Raycast(r, hit, dist))
{
//point is inside other.
} else {
//point is outside other.
}
Thank you for your answer, but could it be that this script only works when the point is touching the collider, not when it is inside?
Vector3.one doesn't seem like it is pointing in the right direction? Also, point is not defined?
Answer by cregox · Apr 09, 2013 at 07:16 PM
Here's my version of Mirror's idea, applied in Drakhar's script. I'm also using a layer mask here to prevent further issues on the detection. Hope you enjoy! :)
edit: Unfortunately, RaycastAll doesn't bring all hits in the same collider - just the first one. So this simply doesn't work, while RaycastAll isn't fixed or I find / create a new implementation of it.
P.S. to both: if you feel this is good, be free to assimilate this on your answer to better organize this "thread" dudes.
///
/// Check if point is contained inside a collider.
/// It doesn't necessarily work for concave colliders, as RaycastAll only brings 1 hit per collider.
/// Usage example, assuming they're not null:
/// Collider col; Vector3 point;
/// if (col.Contains(point)) print("collider contains point");
///
static public bool Contains (this Collider col, Vector3 point) {
// Use collider bounds to get the center of the collider.
Vector3 center = col.bounds.center;
// Get direction for later casting a ray from point to center . . .
Vector3 direction = center - point;
// Attempt to prevent raycast to hit any other collider.
int originalLayer = col.gameObject.layer;
int unusedLayer = 28; // random assumption
col.gameObject.layer = unusedLayer;
LayerMask mask = 1 << col.gameObject.layer;
// If we hit the collider any odd number of times, point is probably inside.
// But keep in mind RaycastAll doesn't bring more than 1 hit per collider, so this is just here for future implementation.
int hits = Physics.RaycastAll(point, direction, Mathf.Infinity, mask).Length;
bool inside = (hits % 2) == 0; // Keep in mind "Length" begins at 0.
// Just recover original collider's layer.
col.gameObject.layer = originalLayer;
return inside;
}
This system would only work for small $$anonymous$$ority of meshes. You only check once for the skin of the object with a Physics.RaycastAll, This is problematic because the skin of a mesh is only 1 way. You have to check from both sides in order to see it. Because you only check from one side, your calculation only works when the point of interest is the center of a convex shape. Also, Physics.RaycastAll only sees each object once. If the ray overlaps with the same object 5 times, it will not return all intersects with the object, only the first will be returned.
@$$anonymous$$irrorIrorri$$anonymous$$ From my brief experience with few trials, I have to disagree. Physics.RaycastAll
seem to be returning an array with every time it hits any "skin", as you call it. I've tested this on many concave meshes and it always worked. And I came with this idea the same way you did, because I was having issues with the first implementation which didn't account for concave shapes.
Just tested it. RaycastAll only gives one hit per collider. In addition, when you do your RaycastAll, you do not set a distance. This will just shoot a beam straight through the object with nothing to stop it. You need to set a distance for it to actually stop at the point of interest.
@$$anonymous$$irrorIrorri$$anonymous$$ I must admit now I see something wrong there, and I'll spend some time to check exactly what while making tests to make sure this works... But I still don't think raycastall gives only 1 hit per collider and I don't think we need to set a distance to make it stop. It goes from origin to infinite, and if it really works like that, it sounds better than making a distance to "near infinite" as you suggest. A whole different problem (I now see) is that maybe it doesn't allow to use masks without setting a distance.
@$$anonymous$$irrorIrorri$$anonymous$$ yes, indeed RaycastAll
only gives 1 hit per Collider
, sadly. $$anonymous$$y confusion there was because I was trying with concave objects and shapes which had many colliders, so I don't really have any good test case. As for the distance, it can be set to Infinity anyway. I'll updated my answer. (BTW, you should have just linked me your previous posts on this subject! http://answers.unity3d.com/questions/386698/detecting-how-many-times-a-raycast-collides-with-a.html )
Your answer
Follow this Question
Related Questions
How to know if a collider has a rigid body inside it or not? 1 Answer
Detecting a collider in 3D space. 1 Answer
How to get a collider from point? 1 Answer
2D Array for pathfinding 0 Answers
Finding if point is contained within irregular mesh 3 Answers