Search Unity

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

Discussion in 'Scripting' started by wingsneon, Feb 5, 2024.

  1. wingsneon

    wingsneon

    Joined:
    Jul 5, 2018
    Posts:
    23
    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,

    Code (CSharp):
    1. Vector3 GetGroundHeight(Vector3 origin)
    2.     {
    3.         Vector3 pos = new Vector3(0, 0, 0);
    4.  
    5.         RaycastHit hit;
    6.         if (Physics.Raycast(origin, Vector3.down, out hit, Mathf.Infinity, groundMask))
    7.         {
    8.             print($"ground pos: {hit.point}");
    9.             pos.y = hit.point.y;
    10.         }
    11.         return pos;
    12.     }
    Example of the location of the origin, ignore the gizmo's raycast, it has nothing to do with the GetGroundHeight function.
    upload_2024-2-5_20-39-25.png

    What it returns in console (y = 0):
    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!
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,656
    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: https://forum.unity.com/threads/raycast-layermask-parameter.944194/#post-6161542



    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:

    Code (csharp):
    1. [MenuItem( "Tools/CastPlayerSpawnsToGround")]
    2.     static void CastPlayerSpawnsToGround()
    3.     {
    4.         var candidates = SpaceFlightPlayerSpawnFinder.FindPlayerSpawns(true);
    5.  
    6.         int debugCount = 0;
    7.         foreach( var tr in candidates)
    8.         {
    9.             Vector3 pos = tr.position;
    10.  
    11.             bool hitGround = SpawnUtilities.ProgressiveLiftAndCastDown( ref pos);
    12.  
    13.             if (hitGround)
    14.             {
    15.                 tr.position = pos;
    16.                 debugCount++;
    17.             }
    18.  
    19.             if (!hitGround)
    20.             {
    21.                 Debug.LogWarning( "EditorCastPlayerSpawnsToGround.CastPlayerSpawnsToGround(): Failed to cast!");
    22.                 Debug.LogWarning( "Failed object was named " + tr.name);
    23.             }
    24.         }
    25.         Debug.Log( "Cast a total of " + debugCount.ToString() + " to the ground.");
    26.     }
    And here was my caster:

    Code (csharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public static class SpawnUtilities
    6. {
    7.    public static bool ProgressiveLiftAndCastDown( ref Vector3 position, out Vector3 normal)
    8.    {
    9.        Vector3 pos = position;
    10.  
    11.        normal = Vector3.up;
    12.  
    13.        bool hitGround = false;
    14.  
    15.        float castDistance = 0.0f;
    16.  
    17.        for (int tries = 0; tries < 250; tries++)
    18.        {
    19.            pos += Vector3.up * 1.0f;
    20.  
    21.            castDistance += 2.0f;
    22.  
    23.            Ray ray = new Ray( pos, Vector3.down);
    24.            RaycastHit rch;
    25.            if (Physics.Raycast( ray, out rch, castDistance))
    26.            {
    27.                hitGround = true;
    28.  
    29.                position = rch.point;
    30.  
    31.                normal = rch.normal;
    32.  
    33.                var s = "SpawnUtilities.ProgressiveLiftAndCastDown:hit '" + rch.collider.name + "'";
    34.                s += System.String.Format( ": pos:{0} norm:{1}", position, normal);
    35.                Debug.Log( s);
    36.  
    37.                break;
    38.            }
    39.        }
    40.  
    41.        return hitGround;
    42.    }
    43.  
    44.    public static bool ProgressiveLiftAndCastDown( ref Vector3 position)
    45.    {
    46.        Vector3 normalDummy = Vector3.zero;
    47.  
    48.        bool hitGround = ProgressiveLiftAndCastDown( ref position, out normalDummy);
    49.  
    50.        return hitGround;
    51.    }
    52. }

    Above code from Jetpack Kurt Space Flight:

     
    Last edited: Feb 6, 2024
    wingsneon likes this.
  3. wingsneon

    wingsneon

    Joined:
    Jul 5, 2018
    Posts:
    23
    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
    Code (CSharp):
    1.  Vector3 GetGroundHeight(Vector3 origin)
    2.     {
    3.         Vector3 pos = new Vector3(0, 0, 0);
    4.         RaycastHit hit;
    5.  
    6.         if (Physics.Raycast(origin, Vector3.down, out hit, Mathf.Infinity, groundMask.value))
    7.         {
    8.             print($"ground pos: {hit.point}");
    9.             Debug.DrawRay(origin, Vector3.down * hit.distance, Color.yellow);
    10.             Print(pos);
    11.         }
    12.         return pos;
    13.     }
    The ray seems to be working properly!
    upload_2024-2-5_23-45-4.png


    But then... (
    Print(pos)
    )
    upload_2024-2-5_23-46-31.png
     
    Last edited: Feb 6, 2024
  4. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,797
    You never assign anything to
    pos
    ...
     
    wingsneon and Kurt-Dekker like this.
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,656
    I think you took out the
    pos.y = hit.point.y
    in your second code example. :)

    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...
     
  6. wingsneon

    wingsneon

    Joined:
    Jul 5, 2018
    Posts:
    23
    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:

    Code (CSharp):
    1.     public LayerMask groundMask;
    2.     // Returns the distance between the position of origin and ground
    3.     float GetGroundDistance(Vector3 origin)
    4.     {
    5.         float height = 3;
    6.         RaycastHit hit;
    7.         if (Physics.Raycast(origin, Vector3.down, out hit, Mathf.Infinity, groundMask.value))
    8.         {
    9.             height = hit.distance;
    10.         }
    11.         return height;
    12.     }
    13.  
    14.  
    15.     private Vector3 thisCube; // script's attached to this obj
    16.     private Vector3 randomPos = new Vector3(0f, 0f, 0f);
    17.     // Generates a random position in Z and X axis
    18.     // keeping a certain height from the ground
    19.     public Vector3 GetRandomPos(float setHeight)
    20.     {
    21.  
    22.         // Generates a random position within the gameobject volume
    23.         Vector3 cubePosition = gameObject.transform.position;
    24.         float randomX = Random.Range(cubePosition.x -thisCube.x / 2f, cubePosition.x + thisCube.x / 2f);
    25.         float randomZ = Random.Range(cubePosition.z-thisCube.z / 2f, cubePosition.z+thisCube.z / 2f);
    26.  
    27.         // I opted to update the randomPos value, instead of creating a new vector everytime.
    28.         randomPos.x = randomX;
    29.         randomPos.z = randomZ;
    30.         randomPos.y = 3f; // presetting this value fixed it from returning different y variations.
    31.         randomPos.y = - GetGroundDistance(randomPos) + setHeight; // Stick it to the ground then apply height
    32.  
    33.         return randomPos;
    34.     }
    35.  
     
    Last edited: Feb 8, 2024
  7. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    916
    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.