- Home /
Is it possible to allow a raycast to pass through a collider to hit things behind it?
I have a scenario in which sometimes there's an invisible collider in front of objects that need to be clicked on. It is necessary that this invisible object has a collider. However even using a layermask the invisible object still blocks rays. The layer mask simply stops it from returning true for a collision.
It is not an option to turn on and off the collider of the invisible object at appropriate times as 1 or more objects respond to an OnTriggerExit with the invisible object.
What I need to know is, can I make a ray pass through a collider ignoring it completely until it either hits an intended collider or hits max distance?
Any help is much appreciated : ]
Hi! I'm fighting with the same problem. Anyone found a solution yet?
@Owen Reynolds's answer works fine - but pay attention to the way the mask skip is formed: it's the complement (~) of the layers to be skipped. $$anonymous$$y answer works fine too, but takes a little more time - anyway, it's a solution when you don't want (or can) use different layers.
Look into what a layermask is. It is the fourth argument to an instance of Physics.raycast. Using it is a bit nuanced, but if you read the documentation you will understand it: https://docs.unity3d.com/ScriptReference/Physics.Raycast.html
Answer by Owen-Reynolds · Oct 21, 2011 at 03:57 PM
I just ran a quick check -- rayCasting through a custom layer (one you create) works just fine. Made four cubes lined up on z, added custom layers 8 and 9 (skip and skip2, but the names don't matter,) lined them up on z and added a raycast script to the start cube:
Cubes: start-> wall1 wall2 target
Layer: default skip(8) skip2(9) default
if(Input.GetKey("space")) {
RaycastHit H;
// goes through wall1 (layer 8,) hits wall2:
if(Physics.Raycast(transform.position, transform.forward, out H, 100, ~1<<8))
Debug.Log(H.transform.name);
// goes through both walls (layer 8 and 9) and hits target:
int skip1n2 = ~((1<<8)|(1<<9));
if(Physics.Raycast(transform.position, transform.forward, out H, 100, skip1n2))
Debug.Log(H.transform.name);
// The previous hits ignoreRaycast. This skips 8,9 and ignoreRC(layer 2):
int skip = ~((1<<8)|(1<<9)|(1<<2));
if(Physics.Raycast(transform.position, transform.forward, out H, 100, skip))
}
http://unity3d.com/support/documentation/Components/Layers.html has the official example.
I understand bit shifting, what I was trying to do simply didn't work until i used unity's build in ignore raycast layer.
Answer by aldonaletto · Oct 21, 2011 at 03:10 AM
I can think of a few solutions for this problem:
1- Disable the "forbidden" object's collider, do the raycast and enable the collider. If you do this in the same function, nobody else will be affected. This may be a solution when you have only one forbidden object, but can be awkward if there are several of them.
2- Use RaycastAll: it returns an array with all hits, and you can ignore the forbidden objects until you find a valid one. This is more time expensive, specially if the raycast is infinite or too long:
var hit: RaycastHit; // the hit info (if any) will be returned here
var nonoTag: String; // define the forbidden tag here
// specify the ray and the max distance
function XRaycast(ray: Ray, dist: float): boolean {
var hits = Physics.RaycastAll(ray, dist); // get all hits
for (hit in hits){ // check hits until a valid one is found
if (hit.collider.tag != nonoTag) return true;
}
// if no valid hit found, return false
return false;
}
3- Do a Raycast and check the object hit: if it's a forbidden one, change the origin to the hit.point plus a little offset in the ray.direction, and repeat the process. This can be done recursively until a valid object is found or the ray hits nothing.
The function below can do this: it does successive Raycasts while finding forbidden objects. If some valid object is found, it returns true; if there are no valid objects, returns false.
var hit: RaycastHit; // the hit info (if any) will be returned here
var nonoTag: String; // define the forbidden tag here
// specify the ray and the max distance
static function XRaycast(ray: Ray, dist: float): boolean {
if (!Physics.Raycast(ray, hit, dist)) return false; // return false if nothing found
if (hit.collider.tag != nonoTag) return true; // return true if valid obj found
var end = ray.GetPoint(dist); // if invalid object hit, set a new
ray.origin = hit.point+0.1*ray.direction; // ray origin 0.1 ahead of the hit.point
dist = Vector3.Distance(end, ray.origin); // update the max distance...
return XRaycast(ray, dist); // and call itself
}
Wwith number 1, shouldn't we be careful about multi-threading?
You can't use multi threading at all in this case as the Unity API is not thread safe. A Raycast can only be executed from the main thread. Furthermore almost all gameobject / transform properties can't even be read from another thread. Unity implemented a check into most API methods / properties which will throw an exception if you try to use it from another thread.
Well that simplifies everything quite a bit. So, you are saying there is no multithreading in unity at all? How do programs run fast enough?
Answer by Ziad · Oct 20, 2011 at 09:37 PM
Yeah sure just give it a layerMask
Something like this
if (Physics.Raycast(ray, out hit, Mathf.Infinity, myLayerMask))
Actually that's the way I spent an hour failing to make work. I came to find out that unity has it's own built in layer called "ignore raycast". I set said invisible object to that layer.
$$anonymous$$aking my own custom layers and separating the object that way does not work. The ray still hits the invisible object and does not continue past it. All that happens is the raycasthit returns false ins$$anonymous$$d of true.
Layermask is what you can HIT, not skip. To skip layer 12, say, use ~(1<<12)
as the layer$$anonymous$$ask (the notes on Layer$$anonymous$$ask have an example and explain it.)
It sort of makes sense if you know that the traditional programmer's definition of a $$anonymous$$ask is often picking things you don't like (you mask them off.)
Answer by Cornelis-de-Jager · Jul 10, 2017 at 03:04 AM
HI,
I struggled with this for a long time. And I finally found how to do it so it works every time. but first for your solution you can set the layer of the object to be ignored to "IgnoreRaycast", however, you do not always want this. So here is the correct way
// Create Ray
Ray ray = new Ray( transform.position, transform.forward);
// Hit info
RaycastHit hit;
// Layer to hit / Layer to ignore
string nameOfLayer = "InvisObjectLayer";
/**
* Note that we use ` ~ ` sign if we want the opposite.
* So we want all the layers accept that particular one.
* If we only want to cast to one layer just remove the ` ~ ` sign
*/
LayerMask layer = ~(1 << LayerMask.NameToLayer(nameOfLayer ));
// Do the raycast - Use if Statement to automatically check if there was a hit
/** IMPORTANT
* We must use the Mathf.Infinty statement. Unity does not have a
* function that accepts only a ray, hitInfo and layer!
*/
if (Physics.Raycast (ray, out hit, Mathf.infinity, layer)) {
if (hit.transform.tag == "Player")...
}
Answer by mast3rillusion · Feb 27, 2015 at 07:38 AM
For C-Sharp:
Ray ray = Camera.main.ScreenPointToRay(new Vector3(Screen.width/2,Screen.height/2,0));
RaycastHit hit;
int layerMask = 1 << 4;//setting the layer mask to layer 4 (water in my case)
layerMask = ~layerMask;//setting the layer mask to "ignore" layer (~)
if (Physics.Raycast(ray, out hit, maxInteractionRange, layerMask))//adding in the layer mask to the raycast and it WILL ignore the 4th layer!
{
//your code here..
}