Why is this raycast returning false?

I have a raycast that is meant to find a random position on the surface of an object (a procedurally generated plane that represents the ground). Since for now the object is a square on the x and z axes, but irregular on the y axis, I’m trying to do this by taking a random point on the x,z plane and then raycasting down from above the plane and using the hit point as a point on the surface of the ground object.

However, the raycast on line 61 is always returning false and I can’t figure out why…any ideas?

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

public class PickupSpawner : MonoBehaviour
{
    [SerializeField]
    public List <GameObject> pickupObjects;
    public Vector2 minMaxSecondsBetweenSpawns;
    public float maxObjectsInScene = 5f;
    public GameObject groundPlane;

    private List<GameObject> activePickupsInScene;
    private bool currentlySpawning = false;

    LayerMask layerMask;

    private void Start()
    {
        layerMask = LayerMask.GetMask("Ground");
    }

    // Update is called once per frame
    void Update()
    {
        activePickupsInScene = GameObject.FindGameObjectsWithTag("Type0").ToList();

        if (activePickupsInScene.Count < maxObjectsInScene && !currentlySpawning)
        {
            StartCoroutine (spawnNewPickup());
        }
    }

    IEnumerator spawnNewPickup()
    {
        currentlySpawning = true;

        int randomItemIndex = Random.Range(0, pickupObjects.Count);

        GameObject newPickup = Instantiate(pickupObjects[randomItemIndex], pickRandomLocation(), Quaternion.identity) as GameObject;
        newPickup.tag = PickupObjects.setPickupType(pickupObjects[randomItemIndex].name);

        float waitCount = Random.Range(minMaxSecondsBetweenSpawns.x, minMaxSecondsBetweenSpawns.y);

        yield return new WaitForSeconds(waitCount);
        currentlySpawning = false;
    }

    private Vector3 pickRandomLocation()
    {
        float terrainScaleX = groundPlane.transform.localScale.x * 0.5f;
        float terrainScaleY = groundPlane.transform.localScale.y * 0.5f;

        Vector3 randomLocation = new Vector3(Random.Range(-terrainScaleX, terrainScaleX), 25f, Random.Range(-terrainScaleY, terrainScaleY));

        Ray ray = new Ray(randomLocation, Vector3.down);
        RaycastHit hit;

        if (Physics.Raycast(ray, out hit, layerMask)) {
            Debug.Log("Ray hit ground plane at " + hit.point);
            return hit.point;
        }
        else
        {
            Debug.Log("Raycast failed!");
            return Vector3.zero;
        }
    }
}

When you procedurally generated the mesh, did you also add a MeshCollider, AND make sure the mesh it uses is the newly-created procedural mesh?

And then also… it might be you want to invert the mask with tilde; I always forget if the mask is “ignore this” or “get this.” You can bit-invert the mask with this code instead for line 21 above:

layerMask = ~LayerMask.GetMask("Ground");

Only difference is the tilde (bitwise not). That is NOT a minus sign!

1 Like

I’ve swapped in a non-procgen ground plane for now to make things simpler.

As for the layer mask, it doesn’t seem to work with or without the tilde…

And you have a layer called “Ground” and you have flagged your procgen collider as such I take it??

Oh, I think I see your problem. It’s the order of the supplied optional arguments.

From this API spec:

I see this prototype, which you are calling:

public static bool Raycast(
  Ray ray,
  out RaycastHit hitInfo,
  float maxDistance = Mathf.Infinity,
  int layerMask = DefaultRaycastLayers,
  QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal);

Note the order of optional arguments. It’s always Best Practices™ to supply the actual name of the optional argument, which also guards you against future name changes or order changes.

You can supply optional names by argName: blah instead of just blah when calling a function, so in this case mark your supplied layerMask (which is interpreted as maxDistance) with layerMask: layerMask.

That worked beautifully, thanks! I had no idea you could do that.

1 Like