Search Unity

  1. If you have experience with import & exporting custom (.unitypackage) packages, please help complete a survey (open until May 15, 2024).
    Dismiss Notice
  2. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice

Sort an array of GameObjects by their Y position [Solved]

Discussion in 'Scripting' started by Ilingis, Mar 15, 2015.

  1. Ilingis

    Ilingis

    Joined:
    Dec 13, 2012
    Posts:
    63
    I have an array of GameObjects (var gameobjects : GameObject[]; ) and I'd like to sort them by their Y position (from the smallest number to the highest). I might know about a difficult way how to do this but I wonder if there is an easy way of doing it? I'd appreciate if anyone could help me with this.
     
  2. Ecocide

    Ecocide

    Joined:
    Aug 4, 2011
    Posts:
    293
    Hi,

    look at this: http://answers.unity3d.com/questions/135561/sorting-an-array-c.html

    The last answer is the right way to go I think (using System.Linq.OrderBy)

    Something like:
    Code (csharp):
    1.  
    2. gameObjectList = gameObjectList.OrderBy(go => go.transform.position.y.ToArray());
    3.  
    Never worked with the Linq namespace though, but I hope this gives you an idea :)
     
    Ilingis likes this.
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,555
    Linq isn't necessarily the write way to go... especially if you don't want a whole new collection. Because linq will create a new enumerable of the elements, that you'd then have to create a new array from, if you wanted to store it.

    If this was going to occur every frame, that's a lot of garbage you're creating.

    Just go with Array.Sort if you want to maintain the existing array.

    https://msdn.microsoft.com/en-us/library/cxt053xf(v=vs.110).aspx

    It does the sorting, the function you pass in just tells the sort algorithm how any 2 elements in the array match. -1 means that first param comes before the second. 0 means it doesn't matter if one comes before the other (they match). 1 means the first param comes after the second.

    So you'd do:

    Code (csharp):
    1.  
    2.     GameObject[] _objects;
    3.  
    4.     void Update()
    5.     {
    6.         System.Array.Sort(_objects, YPositionComparison);
    7.     }
    8.  
    9.     private int YPositionComparison(GameObject a, GameObject b)
    10.     {
    11.         //null check, I consider nulls to be less than non-null
    12.         if (a == null) return (b == null) ? 0 : -1;
    13.         if (b == null) return 1;
    14.  
    15.         var ya = a.transform.position.y;
    16.         var yb = b.transform.position.y;
    17.         return ya.CompareTo(yb); //here I use the default comparison of floats
    18.     }
    19.  
    If you wanted to do linq it'd be:

    Code (csharp):
    1.  
    2. //if you needed to store the result
    3. _objects = _objects.OrderBy(go => go.transform.position.y).ToArray();
    4.  
    5. //if you don't need to store the result and just want to loop on a sorted this time only situation
    6. foreach(var go in _objects.OrderBy(go => go.transform.position.y))
    7. {
    8.      //do stuff
    9. }
    10.  
     
  4. Ilingis

    Ilingis

    Joined:
    Dec 13, 2012
    Posts:
    63
    lordofduct's code did the trick! Thank you, I really appreciate it.

    Thanks to both of you. Unity community is really awesome.
     
    nemesis06bal likes this.
  5. fahimmanowarj5

    fahimmanowarj5

    Joined:
    Mar 2, 2018
    Posts:
    4
    this guys is the best
    if u can plz adopt me lol
     
  6. DrFinger

    DrFinger

    Joined:
    Jan 22, 2018
    Posts:
    1
    Awesome solution from lordofduct. Works perfectly. I may also request adoption
     
  7. antoncastro44

    antoncastro44

    Joined:
    Sep 30, 2022
    Posts:
    4
    I'm trying to use this code to sort the enemy array by distance to my player, and it works fine on start, the enemies are ordered by distance correctly, but it won't update as I move. Just changed the transform.position.y to a sqrMagnitude between a/b and my player. Is there anything I'm missing? Should I be updating something else?
     
  8. SF_FrankvHoof

    SF_FrankvHoof

    Joined:
    Apr 1, 2022
    Posts:
    780
    It won't update unless you call it again. It simply checks distances whenever you call it. There's no magic to make it automatically update.
     
  9. antoncastro44

    antoncastro44

    Joined:
    Sep 30, 2022
    Posts:
    4
    That's why I ask, I know it needs to be called. As I understand it, the Array.Sort is called each frame being on Update, what I'm not sure about is if the YPositionComparison is being called every frame too or not, or if this code is supposed to work just once on Start and I should be updating the comparison code every frame too for it to work
     
  10. SF_FrankvHoof

    SF_FrankvHoof

    Joined:
    Apr 1, 2022
    Posts:
    780
    The YPositionComparison is called for every 2 objects that are compared during the sort.
     
    Bunny83 likes this.
  11. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,555
    Share your code as you have it written and we can better understand the problem you may be having.
     
  12. antoncastro44

    antoncastro44

    Joined:
    Sep 30, 2022
    Posts:
    4

    Code (CSharp):
    1.  
    2.  
    3.    
    4.     void Start()
    5.     {
    6.        enemyList = GameObject.FindGameObjectsWithTag("Enemy");
    7.  
    8.     }
    9.  
    10.     void Update()
    11.     {
    12.        System.Array.Sort(enemyList, DistanceComparison);
    13.  
    14.     }
    15.  
    16.     private int DistanceComparison(GameObject a, GameObject b)
    17.     {
    18.         if (a == null) return (b == null) ? 0 : -1;
    19.         if (b == null) return 1;
    20.  
    21.         var distanceA = (a.transform.position - this.transform.position).sqrMagnitude;
    22.         var distanceB = (b.transform.position - this.transform.position).sqrMagnitude;
    23.         return distanceA.CompareTo(distanceB);
    24.  
    25.     }

    Like I said, the code itself works for the first frame, it will sort the enemies by proximity depending on where the player starts (moving it in the editor before play). What I'm having trouble with is having it update as both the player and the enemies move
     
  13. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,930
    Have you debugged? Is this script even on the thing that is moving? The code certainly assumes so (lines 21 and 22) and you could check this yourself in about 3 seconds but you haven't mentioned it, even after having necro-posted to a 2015 thread now three times.

    Is this script on the GameObject that cares where the distance is measured from or not?

    This is generally how debugging / troubleshooting works:

    You must find a way to get the information you need in order to reason about what the problem is.

    Once you understand what the problem is, you may begin to reason about a solution to the problem.

    What is often happening in these cases is one of the following:

    - the code you think is executing is not actually executing at all
    - the code is executing far EARLIER or LATER than you think
    - the code is executing far LESS OFTEN than you think
    - the code is executing far MORE OFTEN than you think
    - the code is executing on another GameObject than you think it is
    - you're getting an error or warning and you haven't noticed it in the console window

    To help gain more insight into your problem, I recommend liberally sprinkling
    Debug.Log()
    statements through your code to display information in realtime.

    Doing this should help you answer these types of questions:

    - is this code even running? which parts are running? how often does it run? what order does it run in?
    - what are the values of the variables involved? Are they initialized? Are the values reasonable?
    - are you meeting ALL the requirements to receive callbacks such as triggers / colliders (review the documentation)

    Knowing this information will help you reason about the behavior you are seeing.

    You can also supply a second argument to Debug.Log() and when you click the message, it will highlight the object in scene, such as
    Debug.Log("Problem!",this);


    If your problem would benefit from in-scene or in-game visualization, Debug.DrawRay() or Debug.DrawLine() can help you visualize things like rays (used in raycasting) or distances.

    You can also call Debug.Break() to pause the Editor when certain interesting pieces of code run, and then study the scene manually, looking for all the parts, where they are, what scripts are on them, etc.

    You can also call GameObject.CreatePrimitive() to emplace debug-marker-ish objects in the scene at runtime.

    You could also just display various important quantities in UI Text elements to watch them change as you play the game.

    If you are running a mobile device you can also view the console output. Google for how on your particular mobile target, such as this answer or iOS: https://forum.unity.com/threads/how-to-capturing-device-logs-on-ios.529920/ or this answer for Android: https://forum.unity.com/threads/how-to-capturing-device-logs-on-android.528680/

    If you are working in VR, it might be useful to make your on onscreen log output, or integrate one from the asset store, so you can see what is happening as you operate your software.

    Another useful approach is to temporarily strip out everything besides what is necessary to prove your issue. This can simplify and isolate compounding effects of other items in your scene or prefab.

    Here's an example of putting in a laser-focused Debug.Log() and how that can save you a TON of time wallowing around speculating what might be going wrong:

    https://forum.unity.com/threads/coroutine-missing-hint-and-error.1103197/#post-7100494

    When in doubt, print it out!(tm)

    Note: the
    print()
    function is an alias for Debug.Log() provided by the MonoBehaviour class.
     
    antoncastro44 likes this.
  14. antoncastro44

    antoncastro44

    Joined:
    Sep 30, 2022
    Posts:
    4
    Understood, will get to work on it now. I'm terribly sorry, it wasn't my intention to be annoying, I'm still pretty new to both coding and requesting help on the forums, but I couldn't find answers for this anywhere on my own. Thanks for the help and the patience. Apologies once again, will stop replying to this thread
     
  15. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,930
    Awesome! Generally it's ALWAYS best to start your own fresh thread. That way all the people who answered this eight years ago aren't bothered by notification mails for something they have likely long ago forgotten about. Not only that but almost all problems are different, or at least different enough.

    I assume from your posting that you have at least begun to understand the script in question, so let me offer you some extra guidance to bring you over the finish line.

    The way the script above uses Array.Sort() is by supplying a custom sort delegate.

    This delegate is your DistanceComparison() function above, and you see it taking two GameObjects.

    Array.Sort() repeatedly calls that method with every object in the list (which you got with FindGameObjectsWithTag()) and after "doing some things" with that method, that method returns -1, 0 or 1 to Array.Sort().

    What it is doing is computing the distance from
    this.transform
    (aka, the Transform of the GameObject where this script is located) to each enemy A and B, and then returning the difference in distances, eg, which A or B is further / nearer to the this.transform.

    If this script is on some other object, or another enemy, or something that is NOT the player, it obviously won't work because it would be feeding some random data in, not the correct player's location.

    I hope that makes sense. I know it's a lot to firehose in all at once but I feel like you're ready and willing to do it.

    And this is the generic steps involved in using sample code, particularly Step #2:

    Tutorials and example code are great, but keep this in mind to maximize your success and minimize your frustration:

    How to do tutorials properly, two (2) simple steps to success:

    Step 1. Follow the tutorial and do every single step of the tutorial 100% precisely the way it is shown. Even the slightest deviation (even a single character!) generally ends in disaster. That's how software engineering works. Every step must be taken, every single letter must be spelled, capitalized, punctuated and spaced (or not spaced) properly, literally NOTHING can be omitted or skipped.

    Fortunately this is the easiest part to get right: Be a robot. Don't make any mistakes.
    BE PERFECT IN EVERYTHING YOU DO HERE!!


    If you get any errors, learn how to read the error code and fix your error. Google is your friend here. Do NOT continue until you fix your error. Your error will probably be somewhere near the parenthesis numbers (line and character position) in the file. It is almost CERTAINLY your typo causing the error, so look again and fix it.

    Step 2. Go back and work through every part of the tutorial again, and this time explain it to your doggie. See how I am doing that in my avatar picture? If you have no dog, explain it to your house plant. If you are unable to explain any part of it, STOP. DO NOT PROCEED. Now go learn how that part works. Read the documentation on the functions involved. Go back to the tutorial and try to figure out WHY they did that. This is the part that takes a LOT of time when you are new. It might take days or weeks to work through a single 5-minute tutorial. Stick with it. You will learn.

    Step 2 is the part everybody seems to miss. Without Step 2 you are simply a code-typing monkey and outside of the specific tutorial you did, you will be completely lost. If you want to learn, you MUST do Step 2.

    Of course, all this presupposes no errors in the tutorial. For certain tutorial makers (like Unity, Brackeys, Imphenzia, Sebastian Lague) this is usually the case. For some other less-well-known content creators, this is less true. Read the comments on the video: did anyone have issues like you did? If there's an error, you will NEVER be the first guy to find it.

    Beyond that, Step 3, 4, 5 and 6 become easy because you already understand!
     
    antoncastro44 likes this.