Spawn a prefab at the closest GameObject with one of four relevant tags.

I have 4 different tags that I add to a list to know possible spawn locations for my object.

 public List<GameObject>  locationsToSpawn = new List<GameObject>();
 locationsToSpawn.AddRange(GameObject.FindGameObjectsWithTag("tag1"));
 locationsToSpawn.AddRange (GameObject.FindGameObjectsWithTag("tag2")); 
 locationsToSpawn.AddRange(GameObject.FindGameObjectsWithTag("tag3")); 
 locationsToSpawn.AddRange(GameObject.FindGameObjectsWithTag("tag4"));

I want to spawn an new object on the edge of the GameObject in the scene that is closest to my click.

if (Input.GetMouseButtonDown(0)) 
{ 
    clickPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); 
    closestObject(); 
    if(closest.tag == "tag1")
    { 
        objectSpawnPosition = closest.transform.position; 
        GameObject objectInstance = Instantiate(spawnUpPrefab, objectSpawnPosition, Quaternion.Euler(new Vector3(0, 0, 0))); 
    } 
    //repeated for the 3 other tags.
}

This is how I find the closest object (largely stolen from unity documentation)

public GameObject closestObject()
{
    float distance = Mathf.Infinity;
    foreach (GameObject go in locationsToSpawn)
    {
        Vector3 diff = go.transform.position - clickPosition;
        float curDistance = diff.sqrMagnitude;
        if (curDistance < distance)
        {
              closest = go;
              distance = curDistance;
        }
    }
return closest;
}

I have two major problems. It doesn’t always spawn on the closest object, almost ignores tag1 even when pressing directly on that object when one of the others are nearby. Also, I can’t figure out how to properly spawn on the edge, as of now I use objectSpawnPosition.y = closest.transform.position.y + A.BCf; to correct the positioning but I feel like there should be a more effective or more precise way.

Thank you!

You’re not resetting the distance variable (to eg float.MaxValue) at the start of your search. As a result, your function is not searching for the currently closest location to clickPosition, but for the location that clickPosition has been closest to at some point in time.

To see the effect of not resetting it: imagine that on the first search, you find that the position of clickPositionis identical to that of one of the spawn locations. distance will become set to zero and so, on subsequent searches, (curDistance < distance) will never be true. So subsequent searches will never change the value of closest, regardless of where the clickPosition is.

It’s not clear why distance is a member field (assuming it is, you haven’t shown us its declaration)… I would normally use a local variable to keep track of the “closest value found so far” in this situation (and then if it’s needed outside the function, make that happen at the end of the search). Similarly, why does the function use the non-local closest at all? Better to keep the logic of the search function local. If instead you’d written it this way…

public GameObject closestObject()
 {
     GameObject closestObjectSoFar = null;
     float closestDistanceSoFar = float.MaxValue;
     foreach (GameObject go in locationsToSpawn)
     {
         Vector3 diff = go.transform.position - clickPosition;
         float curDistance = Mathf.Abs(diff.sqrMagnitude);
         if (curDistance < closestDistanceSoFar )
         {
             closestObjectSoFar = go;
             closestDistanceSoFar = curDistance;
         }
     }
     return closestObjectSoFar ;
 }

And used it this way…

closest = closestObject(); 
if (closest == null)
{
// throw exception, log error, etc...  
}

Then you’d have picked up straight away on the fact that your search function is failing.