- Home /
Why are raycasts so unreliable?
I am having difficult time dealing with raycasts. I'm trying to use it with layermasks to collide and ignore various objects in the scene. I can't get it to ignore a collider without ignoring all other colliders its supposed to hit...
eg. laser using raycast passes through Player layers (as expected), but then after that passes through everything else (not good).
So I need a bit of a lecture of how to use raycasts and layermasks.
Take the code you posted below, as a comment, and turn it into a question. Write down the values for the public variables. Explain what layers you have set, how objects are set, and where the problem is. Give it a descriptive title, such as "raycast with layermask skips everything."
Answer by Owen-Reynolds · Mar 12, 2014 at 10:50 PM
Yes, raycasts are tricky -- the coding is more difficult, and lots of special rules. But they are reliable, once you figure them out.
You can't see them, ever. And if you use Debug.DrawRay, you can easily show the wrong line, making it even worse.
Returning true/false and also the RayCastHit by reference is a standard trick, but not many people are used to it. The 9 overloads are odd, and it's easy to enter distance where the layer mask should have been. Layermasks are in binary, but layers aren't. Sometimes a raycast is "good" if it hits the right thing, or if it misses everything... .
But treat them as something tricky, and go slow, set up simple tests and print a lot. You can find many, many examples here, of how layer masks work, etc.. . Really, there are dozens of "firing a bullet using a raycast" threads.
Alright, so do you know a site where I can figure out how to ignore one layer and not everything after that?
https://docs.unity3d.com/Documentation/Components/LayerBasedCollision.html
One post that vaulted me past 'how the heck do I use these layers' to 'SWEET!' demonstrated a simple variable definition for my C# class:
public Layer$$anonymous$$ask layermask;
which then put a dropdown list in the Inspector and simplified most of my Raycast issues to check/uncheck, using 'layermask' variable in the Raycast. I don't know if you've already done that, but if not try it out.
I just did some testing it turns out that if I add a layermask parameter, it goes through EVERYTHING. So yeah, what the heck?
Adding it is step #1, did you then go into Inspector and use the dropdown to check/uncheck ?
You are bitshifting (I think it's called) and that may or may not be ok but it's not how I use it. Try this ins$$anonymous$$d
Physics.Raycast (transform.position, transform.forward, out rayHit, maxDistance, mask);
Answer by RyanZimmerman87 · Mar 14, 2014 at 04:42 AM
I feel your pain with RayCasts haha. My game relies entirely on using a ton of them from the player movement commands, selecting objects, firing laser beams, etc.
It is a LOT of work to set up a complicated game relying on so many RayCasts instead of simple collisions and more traditional player movement controls.
For just ignoring one layer and being able to hit others is very simple though once you know how to do it. Once you have a ton of different layer interactions that's when it becomes so tedious to make everything work perfectly.
Here's a little example of how to do what you want if I'm understanding your question correctly.
This example might be a little excessive (meaning some parts may be unnecessary I'd have to test to know for sure) but it works, some of the stuff I'm doing with Layer Masks seems strange but this is what I learned about it so far and it works at least.
//start position of Rayshot
public Transform rayStartPosition;
//ray shot length
float maxRayLength = 50;
//variables to store the rayHit info
GameObject rayHitObject;
String rayStringName;
Vector rayVectorHitPosition;
//declare LayerMask variables for player
LayerMask playerLayerMask;
int playerLayerMaskInt;
void Start()
{
//this is what seems weird but it works
//The "8" is the "User Layer" when you
//click on the "Add Layer" option in Inspector
playerLayerMaskInt = ~(1<<8);
playerLayerMask = playerLayerMaskInt;
}
//casting the ray example
RaycastHit rayHit;
if (Physics.Raycast(rayStartPosition.position, rayStartPosition.forward, out rayHit, maxRayLength, playerLayerMask))
{
//get the gameObject
rayHitObject = rayHit.collider.gameObject;
//get the name of object
rayStringName = rayHitObject.name;
//get the position of the rayhit
rayVectorHitPosition = rayHit.collider.gameObject.transform.position;
}
You can add more layers to ignore by declaring them like this example and simply adding a & between each LayerMask variable so it ignores every layer you want. Example:
if (Physics.Raycast(rayStartPosition.position, rayStartPosition.forward, out rayHit, maxRayLength, playerLayerMask & ignoreRayLayerMask))
So if you do it like that it's actually pretty simple for what you are trying to do, but it is strange to learn it, and setting up an entire game around raycasts is very tedious for something like a Diablo 2 style of gameplay where everything is controlled by mouse clicks, or mobile screen taps.
Interesting, I see you are also doing some kind of shifting on 21. I declare the layermask as you did on 13 but public, which exposes it in Inspector, then I check/uncheck depending on need and then use a line like yours on 28
if (Physics.Raycast(rayStartPosition.position, rayStartPosition.forward, out rayHit, maxRayLength, playerLayer$$anonymous$$ask))
but I don't do the bitshift/int stuff you are doing.
You know I find it strange that its only able to hit things that are NOT checked to hit!!!! WHY UNITY WHY. IS THIS A BUG?
I'm finding out that the 4 factory setting layers work when checked and the user made layers work when unchecked. :| This is messed up unity. ()
You know what I'm starting to think that this layermask raycast is not listening to the layer collision matrix. It only seems to collide with objects that are checked in the gameobject script itself. Now I'm seeing how this works.