Raycast ignore itself

I’ve worked with Raycasts for a long time now, and I understand how it works with layermasks, etc…

But I have a problem right now that I couldn’t find in any question here.

Let’s say I have a sentry gun on a 2D game, which is rotating over time searching for the player. Each frame it tests with a raycast if it sees the player. This raycast is ignoring everything except the layer “Obstacle” which is used on objects which the player can’t go through (It’s a tile based game). The objects with that layer are the level walls, but also some “traps”, and the sentry gun is one of them.

So my problem right now is that I want to shoot a raycast from the sentry gun, but the raycast is hitting the sentry itself, and I can’t create a layer for all the obstacle “traps” and shoot a raycast ignoring everything except that layer because that way the player wouldn’t be able to hide himself from sentrys behind other kind of “traps”.

So right now I’m searching for a way of having a raycast ignore everything except 1 layer, and also ignore 1 object inside that layer.

PS: I know that I can use RaycastAll and then ignore this collider, but I am looking for a more efficient way if it exists.

Unity 4.6.1 has the option turn on/off the ability to detect a collider that overlaps the start of any 2D line/raycast in Edit → Project Settings → Physcis 2D → Raycasts Start In Colliders.


I can see several possibilities:

  1. Does the turret have any colliders on its children that are on the wrong layer?
  2. Are you sure you’re compiling the layer mask correctly? “~(1 << LayerMask.NameToLayer(“Obstacle”))” would give you a mask of every layer except the obstacle layer.
  3. Are you sure its hitting the turret and not something else? Try using Debug.Log(message, object), giving it the object you hit as the second parameter so that when you single click the log message in the editor, it will highlight that object in the hierarchy.
  4. Are you sure you’re casting the ray in the right spot? Try using Debug.DrawLine to draw the raycast in the scene view.

There isn’t a way to have a raycast ignore a specific object unless you want to use RaycastAll (which you don’t). There are valid use cases for RaycastAll, but this isn’t one of them, the layer masking system should work.

Whenever I need to do some custom raycast collider filtering, I use RaycastNonAlloc (works for both 2D and 3D physics). It’s actually RaycastAll but allows you to use pre-allocated array for results (for performance reasons).

The way how it works is that it returns all potential hits, and lets you select the one you like.
The simplest usage would look like this: Collect all hits, sort them by distance. Go through them in the closest-to-farthest order, stop at the one that matches your criteria.

Don’t be worried about the extra cost of collecting all hits. The physics engine has to do very similar thing when doing a regular Raycast anyway.

You can use this method to filter out collider hits based on anything where layers won’t help, eg. texture alpha on the hit position (that’s what I’ve been using it for, mostly)

Some extra tips:

  • Consider filtering out collisions before sorting (see what’s more expensive, filtering or sorting in your use cases)
  • When sorting, don’t even think about doing vector.magnitude! all you need is sqrMagnitude when comparing multiple vector lengths (as sqrt is an one-to-one function), you will save a lot of sqrt’s which are quite expensive.

I had the same problem, this is how I solved it:

Physics2D.Raycast(rayCasterPosition + directionOfRayCast*this.collider2D.bounds.size.magnitude,
directionOfRayCast, distance+1);

directionOfRayCast is normalized.

To put it more simply, I shot the ray outside of my own collider.

My solution was to turn off the collider before the raycast and turn it on at the end:

for (int i = 0; i < sectorCount; i++) 
locations *.GetComponent<CircleCollider2D>().enabled = false;*

RaycastHit2D hit = Physics2D.Raycast (locations .transform.position, Vector2.up);
locations .GetComponent().enabled = true;