Search Unity

Search for closest object with tag

Discussion in 'Scripting' started by Benedikt_, Aug 2, 2015.

  1. Benedikt_

    Benedikt_

    Joined:
    Jul 29, 2015
    Posts:
    4
    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)
    Code (CSharp):
    1.     GameObject FindClosestItem(GameObject closestItem)
    2.     {
    3.         var items = GameObject;
    4.         items = GameObject.FindGameObjectsWithTag ("Item");
    5.         var closestItem = GameObject;
    6.         var distance = Mathf.Infinity;
    7.         var position = Transform.position;
    8.  
    9.         for (var item = GameObject in items)
    10.         {
    11.             var diff = (items.transform.position - position);
    12.             var curDistance = diff.sqrMagnitude;
    13.            
    14.             if (curDistance < distance)
    15.             {
    16.                 closestItem = items;
    17.                 distance = curDistance;
    18.             }
    19.         }
    20.         return closestItem;
    21.     }
    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).
     
  2. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    Code (csharp):
    1.    GameObject FindClosestToTarget(Transform target, string tag)
    2.    {
    3.        GameObject[] options = GameObject.FindGameObjectsWithTag (tag);
    4.  
    5.        if(options.Length == 0)
    6.           return null;
    7.  
    8.        GameObject closest = options[0];
    9.        float closestDistance = Vector3.Distance(target.position, closest.transform.position);
    10.        float thisDistance;
    11.  
    12.        for(int i = 1; i < options.Length; i++)
    13.        {
    14.           thisDistance = Vector3.Distance(target.position, options[i].transform.position);
    15.           if(thisDistance < closestDistance)
    16.           {
    17.               closest = options[i];
    18.               closestDistance = thisDistance;
    19.           }
    20.        }
    21.        return closest;
    22.    }
    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.
     
    Last edited: Aug 2, 2015
  3. Benedikt_

    Benedikt_

    Joined:
    Jul 29, 2015
    Posts:
    4
    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'. :(
     
  4. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    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)

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Linq;
    3.  
    4. public static class ExtGameObject
    5. {
    6.     public static GameObject FindClosestGameObjectWithTag(this GameObject myGameObject, string tag)
    7.     {
    8.         return GameObject.FindGameObjectsWithTag(tag)
    9.                .OrderBy(x => Vector3.SqrMagnitude(x.transform.position - myGameObject.transform.position))
    10.                .FirstOrDefault();
    11.     }
    12. }

    You can use it like this
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class FindClosestWithTag : MonoBehaviour
    4. {
    5.     public string tag;
    6.  
    7.     void Update()
    8.     {
    9.         GameObject closestGameObject = gameObject.FindClosestGameObjectWithTag(tag);
    10.  
    11.         Debug.Log(closestGameObject.name);
    12.         Debug.DrawRay(closestGameObject.transform.position, Vector3.up * 10, Color.green);
    13.     }
    14. }
     
    Last edited: Aug 2, 2015
  5. Fyko-chan

    Fyko-chan

    Joined:
    Sep 29, 2012
    Posts:
    76
    Swap
    Code (CSharp):
    1. Vector3 closestDistance = Vector3.Distance(target.position, closest.transform.position);
    2.        Vector3 thisDistance;
    3.  
    with
    Code (CSharp):
    1. float closestDistance = Vector3.Distance(target.position, closest.transform.position);
    2.        float thisDistance;
    3.  
     
    DonLoquacious likes this.
  6. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    For more performance friendly code (maybe, it's entirely context dependent) you could consider an overlap sphere.
     
  7. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    A trigger sphere around the player that keeps tracks of every object within range is also possible:

    Code (CSharp):
    1. List<GameObject> items = new List<GameObject();
    2.  
    3. void OnTriggerEnter(Collider c) {
    4.     if(c.gameObject.tag == "Item")
    5.         items.Add(c.gameObject);
    6. }
    7.  
    8. void OnTriggerExit(Collider c) {
    9.     if(c.gameObject.tag == "Item")
    10.         items.Remove(c.gameObject);
    11. }
    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.
     
    Kiwasi likes this.