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?
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))
}
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
}
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")...
}