Search for closest object with tag

I know there are tons of posts in the forums (and the documentation), but unfortunately all they give me is head spinning.
so what i need is a function that checks every turn which object with the tag “Item” is closest to the object the script is attached to aka the player.

thats the code i was able to come up with so far (based on the documentation example)

    GameObject FindClosestItem(GameObject closestItem)
    {
        var items = GameObject;
        items = GameObject.FindGameObjectsWithTag ("Item");
        var closestItem = GameObject;
        var distance = Mathf.Infinity;
        var position = Transform.position;

        for (var item = GameObject in items)
        {
            var diff = (items.transform.position - position);
            var curDistance = diff.sqrMagnitude;
           
            if (curDistance < distance)
            {
                closestItem = items;
                distance = curDistance;
            }
        }
        return closestItem;
    }

please keep in mind that i am a newcomer so id be glad if you could use a simple way of the function (even if it is more performance lasting).

   GameObject FindClosestToTarget(Transform target, string tag)
   {
       GameObject[] options = GameObject.FindGameObjectsWithTag (tag);

       if(options.Length == 0)
          return null;

       GameObject closest = options[0];
       float closestDistance = Vector3.Distance(target.position, closest.transform.position);
       float thisDistance;

       for(int i = 1; i < options.Length; i++)
       {
          thisDistance = Vector3.Distance(target.position, options[i].transform.position);
          if(thisDistance < closestDistance)
          {
              closest = options[i];
              closestDistance = thisDistance;
          }
       }
       return closest;
   }

Something like this, I think (untested). You can just pass in a transform of anything you like (current GameObject’s transform an obvious choice) and the tag that you want to check against. Absolutely avoid doing this check every frame though- either check when necessary only (some sort of event), or make a timer in Update, or use a coroutine and set it to loop every second or two while active. Using FindGameObjectsWithTag is pretty expensive.

thank you.

i still get some errors. one is that it can’t implicitly convert type ‘float’ to ‘UnityEngine.Vector3’
got this one twice (one for line 9 and one for line 14)
abd it says that < can’t be applied to operands of type ‘UnityEngine.Vector3’ and ‘UnityEngine.Vector3’. :frowning:

if all you want is something that looks pretty and dont care about garbage collection or performance, then maybe this can work for ya. (have not really tested)

Click for code

using UnityEngine;
using System.Linq;

public static class ExtGameObject
{
    public static GameObject FindClosestGameObjectWithTag(this GameObject myGameObject, string tag)
    {
        return GameObject.FindGameObjectsWithTag(tag)
               .OrderBy(x => Vector3.SqrMagnitude(x.transform.position - myGameObject.transform.position))
               .FirstOrDefault();
    }
}

You can use it like this
Click for example

using UnityEngine;

public class FindClosestWithTag : MonoBehaviour
{
    public string tag;

    void Update()
    {
        GameObject closestGameObject = gameObject.FindClosestGameObjectWithTag(tag);

        Debug.Log(closestGameObject.name);
        Debug.DrawRay(closestGameObject.transform.position, Vector3.up * 10, Color.green);
    }
}

Swap

Vector3 closestDistance = Vector3.Distance(target.position, closest.transform.position);
       Vector3 thisDistance;

with

float closestDistance = Vector3.Distance(target.position, closest.transform.position);
       float thisDistance;
1 Like

For more performance friendly code (maybe, it’s entirely context dependent) you could consider an overlap sphere.

A trigger sphere around the player that keeps tracks of every object within range is also possible:

List<GameObject> items = new List<GameObject();

void OnTriggerEnter(Collider c) {
    if(c.gameObject.tag == "Item")
        items.Add(c.gameObject);
}

void OnTriggerExit(Collider c) {
    if(c.gameObject.tag == "Item")
        items.Remove(c.gameObject);
}

Using that, you can search for the closest item in that list instead of everything in the game. If you’re doing the check every frame, this is theoretically faster than the overlap sphere solution. If you’re doing this every now and then, definitely go with the overlapsphere.

1 Like