Getting height from the ground using Raycasting - returning y = 0 no matter the height!

Hello people, I’m trying to make a function that teleports a gameobject to a random position 3D, in a random X Z direction, but the height (y) must be a fixed height from the ground.

The problem is in the function that should manage the height:
The function will return a Vector3 in which it’s y will be the distance from the origin to the ground.
It casts a ray that starts at origin, pointing down, information goesto hit, and the distance is an infinite value (I might change later to big fixed value).
It returns 0 everytime,

Vector3 GetGroundHeight(Vector3 origin)
    {
        Vector3 pos = new Vector3(0, 0, 0);

        RaycastHit hit;
        if (Physics.Raycast(origin, Vector3.down, out hit, Mathf.Infinity, groundMask))
        {
            print($"ground pos: {hit.point}");
            pos.y = hit.point.y;
        }
        return pos;
    }

Example of the location of the origin, ignore the gizmo’s raycast, it has nothing to do with the GetGroundHeight function.
9626111--1367099--upload_2024-2-5_20-39-25.png

What it returns in console (y = 0):
9626111--1367102--upload_2024-2-5_20-40-9.png

I would like for it to return the actual height between the origin and the ground!
If someone’s curious about how this could help making the height of the gameobject always a fixed position, no matter the ground height variation - my goal is to get the distance between the gameobject and the ground through the raycast, then I’ll set the gameobject height (y) to subtract from the raycast result , which will make the gameobject stick to the ground, then I just add a desired height value like height = 1 or 3.
Thanks in advance!

Are you always hitting something like yourself or something up in the air??

You can print out the hit.transform.name to see.

Also: check your LayerMask is correct: Raycast "layermask" parameter

BTW, I do this same process for my spawnpoint scripts too: I have an editor script that finds all spawnpoints and tries to place them on a surface. I did it by:

In a loop:

  • lift 1 meter
  • cast down 2 meters
  • did I hit something? we’re done, use that hit
  • now increase the lift by 1 meter and the cast by 2 meters

Found the outer wrapper code:

[MenuItem( "Tools/CastPlayerSpawnsToGround")]
    static void CastPlayerSpawnsToGround()
    {
        var candidates = SpaceFlightPlayerSpawnFinder.FindPlayerSpawns(true);

        int debugCount = 0;
        foreach( var tr in candidates)
        {
            Vector3 pos = tr.position;

            bool hitGround = SpawnUtilities.ProgressiveLiftAndCastDown( ref pos);

            if (hitGround)
            {
                tr.position = pos;
                debugCount++;
            }

            if (!hitGround)
            {
                Debug.LogWarning( "EditorCastPlayerSpawnsToGround.CastPlayerSpawnsToGround(): Failed to cast!");
                Debug.LogWarning( "Failed object was named " + tr.name);
            }
        }
        Debug.Log( "Cast a total of " + debugCount.ToString() + " to the ground.");
    }

And here was my caster:

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

public static class SpawnUtilities
{
   public static bool ProgressiveLiftAndCastDown( ref Vector3 position, out Vector3 normal)
   {
       Vector3 pos = position;

       normal = Vector3.up;

       bool hitGround = false;

       float castDistance = 0.0f;

       for (int tries = 0; tries < 250; tries++)
       {
           pos += Vector3.up * 1.0f;

           castDistance += 2.0f;

           Ray ray = new Ray( pos, Vector3.down);
           RaycastHit rch;
           if (Physics.Raycast( ray, out rch, castDistance))
           {
               hitGround = true;

               position = rch.point;

               normal = rch.normal;

               var s = "SpawnUtilities.ProgressiveLiftAndCastDown:hit '" + rch.collider.name + "'";
               s += System.String.Format( ": pos:{0} norm:{1}", position, normal);
               Debug.Log( s);

               break;
           }
       }

       return hitGround;
   }

   public static bool ProgressiveLiftAndCastDown( ref Vector3 position)
   {
       Vector3 normalDummy = Vector3.zero;

       bool hitGround = ProgressiveLiftAndCastDown( ref position, out normalDummy);

       return hitGround;
   }
}

Above code from Jetpack Kurt Space Flight:

1 Like

Did that, it’s hitting the right layer. I used Layermask groundLayer; then set the layermask in the inspector to avoid having to do bit shifting

I also added a yellow debug ray to see the hit trajectory, and surprisingly, it seems that it is working properly, though it’s returning 0 for some reason

 Vector3 GetGroundHeight(Vector3 origin)
    {
        Vector3 pos = new Vector3(0, 0, 0);
        RaycastHit hit;

        if (Physics.Raycast(origin, Vector3.down, out hit, Mathf.Infinity, groundMask.value))
        {
            print($"ground pos: {hit.point}");
            Debug.DrawRay(origin, Vector3.down * hit.distance, Color.yellow);
            Print(pos);
        }
        return pos;
    }

The ray seems to be working properly!
9626360--1367168--upload_2024-2-5_23-45-4.png

But then… (Print(pos))
9626360--1367171--upload_2024-2-5_23-46-31.png

You never assign anything to pos

2 Likes

I think you took out the pos.y = hit.point.y in your second code example. :slight_smile:

I still have no idea why your first post code wouldn’t work… I suspect something else is setting the y to zero outside of the first function…

I already solved it, really don’t know why it wasn’t working with hit.point.y, but instead I used hit.distance

so height = hit.distance did the trick.

Here’s my code in case someone’s wondering:

    public LayerMask groundMask;
    // Returns the distance between the position of origin and ground
    float GetGroundDistance(Vector3 origin)
    {
        float height = 3;
        RaycastHit hit;
        if (Physics.Raycast(origin, Vector3.down, out hit, Mathf.Infinity, groundMask.value))
        {
            height = hit.distance;
        }
        return height;
    }

 
    private Vector3 thisCube; // script's attached to this obj
    private Vector3 randomPos = new Vector3(0f, 0f, 0f);
    // Generates a random position in Z and X axis
    // keeping a certain height from the ground
    public Vector3 GetRandomPos(float setHeight)
    {

        // Generates a random position within the gameobject volume
        Vector3 cubePosition = gameObject.transform.position;
        float randomX = Random.Range(cubePosition.x -thisCube.x / 2f, cubePosition.x + thisCube.x / 2f);
        float randomZ = Random.Range(cubePosition.z-thisCube.z / 2f, cubePosition.z+thisCube.z / 2f);

        // I opted to update the randomPos value, instead of creating a new vector everytime.
        randomPos.x = randomX;
        randomPos.z = randomZ;
        randomPos.y = 3f; // presetting this value fixed it from returning different y variations.
        randomPos.y = - GetGroundDistance(randomPos) + setHeight; // Stick it to the ground then apply height

        return randomPos;
    }

You were confusing what “hit.point” is. Its just telling you the world coordinate where the impact happened, its not the local offset from your raycast origin.