Find game object with highest y value

Hi there,

iam quite new (first post… :smile:) to Unity and programming and I hope you can help me with the following problem:

I want to find the GameObject with the greatest Y-Value and tag “Filling” and assign it to GameObject highestFilling.

This is what I got so far:

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

public class FindHighestFilling : MonoBehaviour {

    void Update ()
    {
        GameObject[] fillings = GameObject.FindGameObjectsWithTag ("Filling");
        GameObject highestFilling;
        fillings = fillings.OrderBy(filling => filling.transform.position.y).ToArray();
        // Here is where I struggle...How can I assign highestFilling to the GameObject with the highest y?
    }
}

Thanks in advance! :slight_smile:

You’ve already done the hardest part. After you sort the array, your highest object should be either the first or the last object in the array depending on how the sorting algorithm works (I’m assuming last).

If first:

highestFilling = fillings[0];

If last:

highestFilling = fillings[fillings.Length-1];

As an aside, if you’re doing other things with this sorted array then carry on, but “sorting an array” is a lot more CPU-time-consuming than “finding the largest value of an array” and this would be an opportunity for optimization. (For that matter, FindGameObjectsWithTag is not great for speed either.) This is a typical algorithm I use to find the highest/lowest value in an array:

GameObject[] fillings = //your array here
GameObject highestFilling = null;
float highestFillingPosition = -99999f; //start with a value that could never be higher than all your objects
for (int f=0;f<highestFilling.Length;f++) {
float thisY = fillings[f].transform.position.y; //cache this, because calculating it twice is also slower than need be
if (thisY > highestFillingPosition) {
highestFillingPosition = thisY;
highestFilling = fillings[f];
}
}
2 Likes

Sorry for my slow response - busy week :confused:
Thanks for your code! :slight_smile: Problem solved :smile:

But I got another related question:
How do I change the color of the fillings object that is currently the highestFilling (and change it back, once it’s no the highest anymore)?

You can have a property on your FindHighestFilling script:

public class FindHighestFilling
{
     public Color highlightedColor;
     public Color defaultColor;

     private GameObject m_highestObj = null;
     public GameObject HighestObject
     {
          set
          {
               OnChangeHighestObject(m_highestObj, value);
                m_highestObj = value;
          }
          get
          {
                return m_highestObj;
          }
     }

     void OnChangeHighestObject(GameObject oldValue, GameObject newValue)
     {
          if (oldValue != null && oldValue != newValue)
          {
               ColorScript script = oldValue.GetComponent<ColorScript>();
               if (script != null)
               {
                    script.color = defaultColor;
               }
          }
       
          if (newValue != null && newValue != oldValue)
          {
               ColorScript script = newValue.GetComponent<ColorScript>();
               if (script != null)
               {
                    script.color = hightlightedColor;
               }
          }
     }
}

Once you set the new value, old value will have its color changed. Of course, I placed ColorScript as you didn’t mention how you change colors, via script or material so you have to manually fix that.

1 Like

I might messed things up. It compiles but doesnt change the color. This is what I have (caution: errors ahead):

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

public class FindHighestFilling : MonoBehaviour {

    public Color highlightedColor;
    public Color defaultColor;

    private GameObject highestFilling = null;
    public GameObject HighestFilling
    {
        set
        {
            OnChangeHighestObject(highestFilling, value);
            highestFilling = value;
        }
        get
        {
            return highestFilling;
        }
    }

    void Update ()
    {
        FindHighest ();
    }

    void FindHighest() 
    {
        GameObject[] fillings =  GameObject.FindGameObjectsWithTag ("Filling");//your array here
        float highestFillingPosition = -99999f; 
        for (int f=0;f<fillings.Length;f++)
        {
            float thisY = fillings[f].transform.position.y; 
            if (thisY > highestFillingPosition)
            {
                highestFillingPosition = thisY;
                highestFilling = fillings[f];
            }
        }
    }

    void OnChangeHighestObject(GameObject oldValue, GameObject newValue) 
    {
        if (oldValue != null && oldValue != newValue)
        {
            GetComponent<SpriteRenderer>().color = defaultColor;
        }
       
        if (newValue != null && newValue != oldValue)
        {
            GetComponent<SpriteRenderer>().color = highlightedColor;
        }
    }
}

Color did not update because you set the value directly to the variable, not property. You have to write:

    void FindHighest()
    {
        GameObject[] fillings =  GameObject.FindGameObjectsWithTag ("Filling");//your array here
        float highestFillingPosition = -99999f;
        for (int f=0;f<fillings.Length;f++)
        {
            float thisY = fillings[f].transform.position.y;
            if (thisY > highestFillingPosition)
            {
                highestFillingPosition = thisY;
                HighestFilling = fillings[f];   // Notice the Capital first letter
            }
        }
    }

If you plan to use properties, it would be handy to have a small prefix to denote it is for private variable. I usually use “m_” so you can try like:

private GameObject m_fillingObj;
public GameObject HighestFilling
{
     set
     {
          m_fillingObj = value;
     }
     get
     {
          return m_fillingObj;
     }
}

Changed it to:

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

public class FindHighestFilling : MonoBehaviour {

    public Color highlightedColor;
    public Color defaultColor;

    private GameObject m_fillingObj;
    public GameObject HighestFilling
    {
        set
        {
            OnChangeHighestObject(m_fillingObj, value);
            m_fillingObj = value;
        }
        get
        {
            return m_fillingObj;
        }
    }


    void Update ()
    {
        FindHighest ();
    }

    void FindHighest() 
    {
        GameObject[] fillings =  GameObject.FindGameObjectsWithTag ("Filling");//your array here
        float highestFillingPosition = -99999f; //start with a value that could never be higher than all your objects
        for (int f=0;f<fillings.Length;f++)
        {
            float thisY = fillings[f].transform.position.y; //cache this, because calculating it twice is also slower than need be
            if (thisY > highestFillingPosition)
            {
                highestFillingPosition = thisY;
                HighestFilling = fillings[f];
            }
        }
    }

    void OnChangeHighestObject(GameObject oldValue, GameObject newValue)  // Do I have to ad
    {
        if (oldValue != null && oldValue != newValue)
        {
            HighestFilling.GetComponent<SpriteRenderer>().color = defaultColor; // is this correct to change the color via the SpriteRenderer?
        }
       
        if (newValue != null && newValue != oldValue)
        {
            HighestFilling.GetComponent<SpriteRenderer>().color = highlightedColor;
        }
    }
}

Compiling yields the following error message:

NullReferenceException: Object reference not set to an instance of an object
FindHighestFilling.OnChangeHighestObject (UnityEngine.GameObject oldValue, UnityEngine.GameObject newValue) (at Assets/Scripts/FindHighestFilling.cs:54)
FindHighestFilling.set_HighestFilling (UnityEngine.GameObject value) (at Assets/Scripts/FindHighestFilling.cs:15)
FindHighestFilling.FindHighest () (at Assets/Scripts/FindHighestFilling.cs:40)
FindHighestFilling.Update () (at Assets/Scripts/FindHighestFilling.cs:27)

Got any suggestions how to fix this? :slight_smile:

void OnChangeHighestObject(GameObject oldValue, GameObject newValue)  // Do I have to ad
    {
        if (oldValue != null && oldValue != newValue)
        {
            oldValue.GetComponent<SpriteRenderer>().color = defaultColor;
        }
      
        if (newValue != null && newValue != oldValue)
        {
            newValue.GetComponent<SpriteRenderer>().color = highlightedColor;
        }
    }

In your previous code you were calling the property, which returned null at that time obviously. Idea behind this function is to change first game objects color to default, and seconds color to highlighted. If you noticed in your previous code, you weren’t using your parameters at all! :rage:

1 Like

uhh…you are right :smile: Thanks man, now it works as it should.

You can also use LINQ to sort your array.

var go = fillings.Select((value, index)=>new{value, index}).OrderByDescending(vi => vi.value).First();