RayCast Problems Searching for Water.

So thirsty…

I have read posts, watched videos but I cannot get my ray casting water code right.
I got the ground check working no problem, but this is smacking me around a bit.

What I am “trying to do is” raycast up and if I hit the water plane, switch to swimming.
Want to do a distance thing that if the player and the near the surface vs underwater.

I have tried layer masking (c# - Using Layers and Bitmask with Raycast in Unity - Stack Overflow)

Creating a point in front of my character and starting from there.

But no matter what I do I hit the player and not the water.

I am using Aquas Water, but this happens even if I use the basic.
The water has both the water Layer assigned and the tag of water.
The player has both the Player Layer assigned, and a tag of player.

I have looked at a number of posts here and on stack but none of the things they are saying seem to work.
I am definitely missing something in my understanding.

thank you for taking the time to clear the dust out of my canteen and help me find water.

void CheckIsSwimming() { Color waterRayColor = Color.blue;

//Exclude Player Mask
int layerMaskIndex = LayerMask.NameToLayer(“Player”);
int layerMask = 1 << layerMaskIndex;

int distanceToWater = 30;

Vector3 startPos = transform.position;

if (GameManager.Instance.IsDebugMode)
{
Debug.DrawRay(startPos, -Vector3.down * distanceToWater, waterRayColor);
}

RaycastHit hit;
if (Physics.Raycast(startPos, -Vector3.down, out hit, distanceToWater, layerMaskIndex))
{
Debug.Log(“hit:” + hit.collider.tag);

if (hit.collider.tag == “Water”)
{
Debug.Log(“Swimming.” + hit.collider.tag);
//TODO: when this works determine distance to hit for surface vs underwater swimming.
}
}
else
{
//Debug.Log(“Not Swimming.”);

}
}

This is the opposite of excluding the player mask. You’re only searching for hits with the player. If you want to search for the water layer only, use

int waterLayerIndex = LayerMask.NameToLayer (“Water”);
int layerMask = 1 << waterLayerIndex;

instead.

Thank you for the quick response. I made those changes and while it no longer finds the player, it never collides with anything. :frowning:

Make sure your other paramters are correct. First thing I notice is your direction paramter: it should just say Vector3.up, thus changing the line from

Vector3 dir = transform.position + (Vector3.up * maxDistanceToWater);

to

Vector3 dir = Vector3.up;

Also check your other parameters. Maybe your transform.position is offset or maxDistanceToWater is not set properly or something similiar.

transforms all seem fine. code seems ok. Just don’t get it.

  1. Can you post your current version of the code? That’s easier to work with compared to the version above
  2. Is your water set up correctly (mainly the Collider)?

I have a box collider on the water, that is a trigger. I am using alot of paid animations for a mechanim root animation system. I’ll have to strip everything out. Going to try a different scene first, with simpler code to isolate the problem first.

  1. Use LayerMask.GetMask instead of fiddling with bit shifting operation.
  2. post your updated code with a code block.
  3. Is the water collider inside the water of outside the water? If it’s inside raycast won’t hit it.

also from physics settings need to have raycast hits triggers (although it might be already on by default).

if water is always below some Y coordinate, could just compare Y positions (instead of using raycast).

I took the code I have , and used it on a new scene. using the basic unity3d standard third person controller.
it works, dump this code on my player object. and custom controller. it does not.
The ground terrain, and water seem to have same settings. So it has to be something else I did. :slight_smile: I’ll add my crap back in, once piece at a time.

Maybe it has something to do with the mechanim, root motion stuff I am using (which I am not even sure I like) maybe it is messing with the transform directions.

I would prefer to not use the y coordinates because I might have caves, mountain lakes, rivers etc. at different levels. The box collider is attached to the AQUAS water prefab and set to be a trigger. I also want to use different animations for surface vs underwater Hit.distance.

And I have to figure this out anyway, for slopes, cover, falling short vs long distances etc. diving etc :slight_smile:

I’ll look into the LayerMask.GetMask tomorrow. :slight_smile:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ColliderCheck : MonoBehaviour
{
    public float maxDistanceToWater = 10;

    public LayerMask waterLayer;
    public bool InWater = false;

    public float distanceToGround = .3f;
    public bool IsGrounded = false;

    public LayerMask groundLayers;


    private void LateUpdate()
    {
        CheckIsSwimming();
        CheckIsGrounded();
    }
    void CheckIsSwimming()
    {
        Color waterRayColor = Color.blue;

        Vector3 startPos = gameObject.transform.position + Vector3.up;

        Debug.DrawRay(startPos, Vector3.up * maxDistanceToWater, waterRayColor);


        RaycastHit hit;
        if (Physics.Raycast(startPos, Vector3.up, out hit, maxDistanceToWater, waterLayer))
        {

            if (hit.collider.tag == "Water")
            {
                InWater = true;
                //TODO: when this works determine distance to hit for surface vs underwater swimming.
            }
            else
            {
                InWater = false;
            }
        }
        else
        {
            InWater = false;
        }  
      
    }

    void CheckIsGrounded()
    {

        Color groundRayColor = Color.yellow;
        Vector3 startPos = transform.position;
        Vector3 dir = transform.position + (Vector3.down * distanceToGround);

            Debug.DrawLine(startPos, dir, groundRayColor);

        RaycastHit hit;
        if (Physics.Raycast(startPos, Vector3.down, out hit, distanceToGround, groundLayers))
        {

            IsGrounded = true;
        }
        else
        {
            IsGrounded = false;

        }
    }
}

Rebuilding my controller script from scratch seemed to work. Not sure why.
But here are the functions in case anyone is interested. Made some tweeks to the start and end of the ground check, and switched from the capsule collider to the charactercontroller (Unity’s) when I was modifying the working to to include steps.

void CheckIsSwimming()
        {
            Color waterRayColor = Color.blue;

            Vector3 startPos = gameObject.transform.position + Vector3.up;

            Debug.DrawRay(startPos, Vector3.up * maxDistanceToWater, waterRayColor);

            RaycastHit hit;
            if (Physics.Raycast(startPos, Vector3.up, out hit, maxDistanceToWater, waterLayer))
            {
                Debug.Log(hit.collider.tag);
                if (hit.collider.tag == "Water")
                {
                    InWater = true;
                    actualDistanceToWater = hit.distance;
                }
                else
                {
                    InWater = false;
                    actualDistanceToWater = 0;
                }
            }
            else
            {
                InWater = false;
            }

            if (InWater && characterStance != CharacterStance.Swimming && wasWater == false)
            {
                SetStance(CharacterStance.Swimming);
            }
            else if( !InWater && characterStance == CharacterStance.Swimming && wasWater == true)
            {
                SetStance( CharacterStance.Standing);
            }

            rigidBody.useGravity = !InWater;
            wasWater = InWater;
        }

        void CheckIsGrounded()
        {

            Color groundRayColor = Color.green;
            Vector3 startPos = transform.position;
            Vector3 dir = transform.position + (Vector3.down * groundCheckDistance);

            Debug.DrawLine(startPos, dir, groundRayColor);

            RaycastHit hit;

            Debug.DrawLine(transform.position + (Vector3.up * 0.1f), transform.position + (Vector3.up * 0.1f) + (Vector3.down * groundCheckDistance), Color.green);

            if (Physics.Raycast(transform.position + (Vector3.up * 0.1f), Vector3.down, out hit, groundCheckDistance, groundLayers))
            {
                IsGrounded = true;
                actualDistanceToGround = hit.distance;
            }
            else
            {
                IsGrounded = false;
                actualDistanceToGround = 0;
            }
        }