Raytrace problem.

Hi all, having a Raycast issue. Yes, I know I typed in the wrong word in the subject, but I can’t edit it to correct it. :wink:

I’m working with SteamVR using HTC Vive. I found this tutorial HTC Vive Tutorial for Unity | Kodeco . In their tutorial things seem to work fine. I incorporated this into my scene. For the most part things work.
Here is my raytrace code.

        if( Controller.GetPress( SteamVR_Controller.ButtonMask.Touchpad ) )
        {
            RaycastHit hit;

            // Shoot a ray from controller. If it hits something make it store the point where it hit and show
            // the laser. Takes into account the layers which can be teleported onto
            if ( Physics.Raycast( trackedObj.transform.position, transform.forward, out hit, 100, teleportMask ) )
            {
                hitPoint = hit.point;
                ShowLaser( hit );
                // Show reticle
                reticle.SetActive( true );
                // Move reticle to raycast location with additional offset to avoid z-fighting
                teleportReticleTransform.position = hitPoint + teleportReticleOffset;
                // Set to true indicating we can teleport there
                shouldTeleport = true;
            }
        }

In my Unity Project I’m using the third party asset Pirates Island. It comes with lots of models and what not. Some of the models are cliffs. The problem I’m facing is that if I point at a cliff which is NOT on my teleportable layer, I’m teleporting right through the cliff and into the inside of the model itself. So, the raytrace is ignoring the cliff and shooting right through it and hitting the terrain. The image shows a given cliff and where I might try to aim my laser for teleporting in VR. I will end up on the terrain, but inside the cliff model.
3030703--226679--RayTraceIssue.PNG

Are there settings on the cliffs I’m supposed to be taking into account? Each cliff is a part of a LOD group. Each cliff LOD group has two children. The cliffs have both mesh renderers and mesh colliders. The mesh colliders have no material associated with them.

Thanks all!

You’ve answered your own question, it seems. The ray only hits stuff in the layermask, so just include the cliff layer.

What I’d suggest would be doing a second raycast along the same ray, but with a different layermask (one that includes the cliff and other obstacles) - if you hit something on the second raycast and hit.distance is < firsthit.distance, then disable teleporting.

But, if I’m not mistaken, the raycast won’t interact with things not on the layermask… And IF, the raycast hits the cliff first, won’t it see that as the object it’s interacting with?

The affect I’m trying for here is, I don’t want to be able to teleport onto a cliff. If I’m point at a cliff, I want my raycast to fail completely and not teleport at all.

Hmmm… Ok, let me wrap my head around that for a second. :slight_smile:

Thought about it. Ok, that makes sense. I’ll work on that and test it out. Seems reasonable.

Is what I’m experiencing right now expected behavior or is it possible I don’t have something setup properly to begin with?

Oh, one more question… Does Unity’s Water4 somehow have a negative impact on Raytracing? Possibly the reflections? I get some odd behaviors with raytracing in my scene. At times, things that are perfectly ok to raytrace and teleport to will fail my raytrace test and I won’t get a laser.

Only things with colliders can affect raycasting, and I don’t think that the water has one. Even if it did, it wouldn’t “reflect” a raycast - there’s nothing that can do that.

One thing I noticed in your original code that could explain weird behavior:

You’re using trackedObj.transform for the position but just transform.forward for the direction… shouldn’t that be trackedObj.transform.forward?

So do a check for what layer you hit before doing the teleport.

/// <summary> Checks to see if a GameObject is on a layer in a LayerMask. </summary>
        /// <returns> True if the provided GameObject's Layer matches one of the Layers in the provided LayerMask. </returns>
        public static bool LayerMatchTest(LayerMask approvedLayers, GameObject objInQuestion)
        {
            return ((1 << objInQuestion.layer) & approvedLayers) != 0;
        }
if( Controller.GetPress( SteamVR_Controller.ButtonMask.Touchpad ) )
        {
            RaycastHit hit;

            // Shoot a ray from controller. If it hits something make it store the point where it hit and show
            // the laser. Takes into account the layers which can be teleported onto
            if ( Physics.Raycast( trackedObj.transform.position, transform.forward, out hit, 100, teleportMask ) )
            {
                hitPoint = hit.point;
                ShowLaser( hit );
                // Show reticle
                reticle.SetActive( true );
                // Move reticle to raycast location with additional offset to avoid z-fighting
                teleportReticleTransform.position = hitPoint + teleportReticleOffset;


                // Set to true indicating we can teleport there
                shouldTeleport = !LayerMatchTest(prohibitedTeleportLayerMask, hit.collider.gameObject);
            }
        }

Honestly I’m not sure, I was just following along with the SteamVR tutorial.
https://www.raywenderlich.com/149239/htc-vive-tutorial-unity

I just tried and apparently not… I got all kinds of wacky issues with my laser going in all different directions.

Thanks LaneFox… I’ll give it a shot!

You can use Debug.DrawLine(trackedObj.transform.position, hit.point) to draw the raycast itself in the scene view, which may help you debug your issue.

Oh no kidding? That’s an awesome little trick!!!

Guys, it would appear as though my raycast just is not picking up the fact that I’m trying to interact with the cliff 3D object at all.

IF, I point my controller at a cliff and there IS terrain behind it, I will get my laser and will get teleported either into the cliff or behind the cliff. IF, I point my controller at a cliff and there is nothing but sky behind it, I don’t get my laser.

For grins, I’ve put a breakpoint on this code, under no circumstance have I been able to see that objInQuestion is a Cliff object.

public static bool LayerMatchTest(LayerMask approvedLayers, GameObject objInQuestion)
{
    return ((1 << objInQuestion.layer) & approvedLayers) != 0;
}

Which is leading me to wonder if it’s because of the LOD group that is parenting the cliffs.

Also, I changed this conditional in the return statement to something a little more readable… At least for me.

return ( ( 1 << objInQuestion.layer ) == approvedLayers.value );

I’ll admit, I’m no pro when it comes to bitshifting. Never had to do any development that needed it. My “guess” my change should be doing the same as yours. While stopped at the breakpoint in the debugger, I was looking at the various variables and their values. It seems as though this would do what I want…

I suspect this puts me in a situation where I can only have 1 teleportable layer. And for now, that’s fine.

None the less, in neither situation were the cliff objects being picked up by the raycast.

That might give you the right answer, but only if that’s the only layer enabled in the LayerMask.

A LayerMask is a series of bits; the fact that it can be represented as a numerical value (as in .value) is entirely coincidental and honestly should almost never be used.

So you have (1 << objInQuestion.layer). This will give you a large number, but this number only makes sense in binary. If your .layer is layer 12, then (1 << 12) will look like:

00000000000000000001000000000000

It literally shifts the bit over 12 spaces.

If we imagine a LayerMask with layers 3, 4, 5, 10, and 12 checked, then that LayerMask will probably look like:

00000000000000000001010000111000

The single & operator is a “bitwise and”. It will go bit by bit, and will only result in a 1 for that bit if both sides have a 1 in that bit. That’s how it’s used here. If the LayerMask has a 1 in spot 12, then it’ll line up with what is now a 1 in spot 12 thanks to (1<<12). The resulting value will have a 1 there; therefore, it’s nonzero; therefore, it evaluates to “true” in the if statement.

PS: You can also use | as a “bitwise or”, which will let you combine multiple layers into one LayerMask. (1000 | 0001) = 1001. (in binary, of course) If you want to generate the mask I wrote above:

LayerMask mask = (1<<12) | (1<<10) | (1<<5) | (1<<4) | (1<<3);
1 Like

Hi StarManta… Thanks for the less. I used to know this stuff SO MANY YEARS ago. Like 20 years ago. But, all my development up until recently has been database driven apps, have never had a need to use bit shifting, bitmasks and what not.
As far as creating my layermask… The script itself has a Layermask object

public LayerMask teleportMask;

Here are my layers
3031010--226716--Layers.PNG
Then in the inspector, I get this
3031057--226728--layermask.png

I’m assuming this would have the same end result as what you showed in script for creating the mask…

Yeah, pretty much. I mean half the point of the LayerMask class is having that nice dropdown interface for editing them :slight_smile:

Any thoughts on why my raycast is completely ignoring my cliffs? Again, as I understand it, the raycast will stop once it hits something…

For grins I added this code Debug.Log(hit.collider.gameObject.name);
I looked at the Console output. Even though I pointed directly at the cliff, I’m still getting what’s behind it. The terrain.

3031031--226719--ConsoleOutput.PNG

Make sure the cliffs have colliders and their layer set.

It should work fine if you use a prohibited teleport mask and check it via the code I posted. Then you can have roughly everything in the teleportable mask so the ray will hit, then approve/deny the teleport by checking what you hit against the prohibited mask.

As mentioned, the cliffs in the Pirates Island asset pack are part of a LOD group. The very top level LOD does NOT have a collider on them. But the children do. So, for grins, I put a collider on it, but still no go.

And for grins, here is the layermask… I’m only looking at 1 layer right now.
3031057--226728--layermask.png

I guess I’m not being clear.

You only have teleportMask, so the Ray isn’t hitting it because the mask only has teleportable in it. The cliffs are on the nonteleportable layer. The Ray can’t hit them.

That’s why you need the prohibited teleport mask, but comparing layers is a pain in the neck with strings so I posted the bit shift comparison.