- Home /
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 declare the variable "objectSpawnPosition" but you don't use it in the Instantiate call. Where are you using it?
Now that's fixed. Just an error from when I rewrote the names to be more clear when asking for help.
Answer by Bonfire-Boy · Jan 31, 2019 at 12:19 PM
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 clickPosition
is 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.
And a $$anonymous$$or point... you don't need to take the absolute value of a squared magnitude.
Yes, I missed including my "float distance = $$anonymous$$athf.Infinity". Now that's included. Thanks for trying to help me with that. The problem isn't really that the search function is failing all the time, it's just not precise enough. I did try your approach though, and as you've set "closestDistanceSoFar = curDistance;" in a "if (curDistance < closestDistanceSoFar )" so it just finds an element once (the incorrect one) and then never changes. I tried correcting this but it did not improve my results unfortunately.
Your comment has made me realise that I'd put go
where I should have put clickPosition
in my description. I've corrected that. I can't quite see what you mean about the logic, though... closestDistanceSoFar
is keeping track of the closest distance so far, so when it's greater than the current distance (that of the spawn location currently being looked at), you do want to update it. I've not tested the code so I can't 100% rule out the possibility that I've got a blind spot.
When reading through your code again I'm not sure what I was referring to either tbh. I think I just made an assumption from the behaviour I encountered when testing your code. No matter where I clicked it would just continue spawning at the gameobject it spawned the first prefab on, not one of the closest ones, but I can't figure out why that would happen.
The code I have now has decent precision sometimes and sometimes it just picks the tag that is 5cm away ins$$anonymous$$d of the one that is 0.5cm away.
Your answer
Follow this Question
Related Questions
How can I detect multiple triggers on same gameobject at once? 3 Answers
Unity3D Shooter: Using tags to switch level after killing all enemies. 1 Answer
Make different sounds play depending on which item collides with player 0 Answers
Collider2D and Z-Axis 0 Answers
How do i destroy a particle when an object is destroyed 1 Answer