Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Question How to get the ground point of a Vector3?

Discussion in 'Scripting' started by billygamesinc, Mar 30, 2024.

  1. billygamesinc

    billygamesinc

    Joined:
    Dec 5, 2020
    Posts:
    349
    What I'm trying to do is spawn monsters/powerups on the ground. Here's how I'm doing it at the moment.

    1. Find a starting point before randomizing, in this case it would be the player's position.
    2. Randomize the z and x axis whilst using the player's Y position. Since I know that the Y is walkable/jumpable.
    3. Raycast upward from that randomized point to find the ceiling point using the obstacle mask
    4. From the ceiling point raycast downward to find the ground point
    5. Overlap sphere to make sure there aren't any others of it's kind in that position.
    I'm open to using NavMeshHit as well if it's easier.

    Find Ground Point:
    Code (CSharp):
    1.     private static Vector3 FindGroundPoint(Vector3 pos, LayerMask obstacleMask)
    2.     {
    3.         //FindCeiling then
    4.         float rayLength = 100;
    5.         float castPosY = rayLength;
    6.         if (Physics.Raycast(pos, Vector3.up, out RaycastHit ceilingHit, rayLength, obstacleMask))
    7.         {
    8.             castPosY = ceilingHit.point.y;
    9.         }
    10.         if (Physics.Raycast(new Vector3(pos.x, castPosY, pos.z), Vector3.down, out RaycastHit groundHit, 1000, obstacleMask))
    11.         {
    12.             return groundHit.point;
    13.         }
    14.         else
    15.         {
    16.             Debug.LogError("ERROR: Ground Not Found");
    17.             return Vector3.zero;
    18.         }
    19.     }
    Find Random Position:
    Code (CSharp):
    1.    public static bool GetRandomPosition(Vector3 startPos, float minDistance, float maxDistance, LayerMask spawnObjectMask, LayerMask obstacleMask, float checkRadius, out Vector3 randomPos)
    2.     {
    3.         float x = Random.Range(minDistance, maxDistance);
    4.         float z = Random.Range(minDistance, maxDistance);
    5.         int xModifier = Random.Range(0, 100) > 50 ? 1 : -1;
    6.         int zModifier = Random.Range(0, 100) > 50 ? 1 : -1;
    7.         randomPos = FindGroundPoint(new Vector3((startPos.x + x * xModifier), startPos.y, startPos.z + (z * zModifier)), obstacleMask);
    8.         Collider[] results = Physics.OverlapSphere(randomPos, checkRadius, spawnObjectMask);
    9.         if (results == null || results.Length == 0)
    10.         {
    11.             return true;
    12.         }
    13.         else
    14.         {
    15.             return false;
    16.         }
    17.     }
     
  2. ArachnidAnimal

    ArachnidAnimal

    Joined:
    Mar 3, 2015
    Posts:
    1,936
    I don't understand the point of raycasting upwards then downwards. Couldn't you just raycast downwards only?
    Is your code not working?
     
    PraetorBlue and Bunny83 like this.
  3. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    8,204
    Yeah what's the actual question here?

    You can probably just pick some arbitrarily high point and raycast infinitely downwards to find a point on the ground.
     
    ArachnidAnimal and Bunny83 like this.
  4. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,114
    Right, the issue is probably that he thinks when his initial point is below the surface we should cast upwards. However that would not work because a raycast can only detect the surface of a collider from the outside. A raycast from the inside will not hit the collider.
     
    ArachnidAnimal and spiney199 like this.
  5. billygamesinc

    billygamesinc

    Joined:
    Dec 5, 2020
    Posts:
    349
    Well in my case I would need to find a ceiling too, so two raycasts just to get a ground point. I was wondering if there was something built in navmesh or unity to do this with less code.
     
  6. billygamesinc

    billygamesinc

    Joined:
    Dec 5, 2020
    Posts:
    349
    In order to make sure it doesn't spawn on top of surfaces instead of under. Think of something like a doorway, i want it to spawn under the doorway
     
  7. ArachnidAnimal

    ArachnidAnimal

    Joined:
    Mar 3, 2015
    Posts:
    1,936
    It still doesn't make sense to do an upward raycast. Do what spiney199 suggested above. Make sure the raycast can pass through everything except spawnable surfaces.
     
  8. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    1,054
    Code (CSharp):
    1. using UnityEngine;
    2. public class Spawner : MonoBehaviour // Spawn monsters somewhere off in the distance
    3. {
    4.     public GameObject monster;
    5.  
    6.     void Start()
    7.     {
    8.         Vector3 pos=Quaternion.Euler(0,Random.Range(0,360),0)*Vector3.right*30; // Get a random distant point
    9.         if (!Physics.SphereCast(transform.position,0.5f,pos,out RaycastHit hit,30)) // Nothing between us and the point?
    10.             if (Physics.Raycast(transform.position+pos,Vector3.down,out hit,2)) // Ground below the point?
    11.                 Instantiate(monster,hit.point+Vector3.up,Quaternion.identity); // RAWR!!
    12.         Invoke("Start",0.5f); // keep doing it
    13.     }
    14. }
     
    Last edited: Mar 31, 2024
    billygamesinc likes this.
  9. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,114
    Well It does make sense when you understand the reason behind the two raycasts. Imagine a 3d level with several different hallways and levels. Choosing a new random location on the height of your player, that potential new location could be in a hallway at a higher elevation. So casting downwards wouldn't get you a proper position. So he first searches for the ceiling and then does another raycast from the ceiling down to find the floor. Though what doesn't really make sense to me is this default value:
    Code (CSharp):
    1. float castPosY = rayLength;
    So when the potential position is below your current height, he would start the downward ray at an arbitrary absolute position that is equal to the ray length, so relative from absolute 0 rayLength upwards. Technically, depending on the level layout, that point at a height of 100 could still be below a potential valid location as this is not relative to the current player position but an absolute height in worldspace.

    I would generally start with a single raycast downwards from the player center height to find the floor. If there is no floor below, do a raycast upwards to find the closest ceiling at this location and then do another raycast down to find the floor below that ceiling point. Just going up a fix amount and casting down would cause many issues if your level has multi layers / levels stacked on top of each other as you would always just hit the top floor.

    Though just picking a random point in a rectangle around the player also isn't a great way to navigate anyways :) However this all depends on the level layout.
     
    PraetorBlue likes this.
  10. billygamesinc

    billygamesinc

    Joined:
    Dec 5, 2020
    Posts:
    349
    Thanks for the detailed explanation. Getting the center position of the capsule collider seems like a way to approach this thanks!