Search Unity

Resolved Best way to sort a list based on an objects variable?

Discussion in 'Scripting' started by PaperMouseGames, May 11, 2020.

  1. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    Hi there! I'm working on a turn based battle system for an RPG and currently my battle system just places all the combatants in a list with an arbitrary order.

    What I want is for my list to compare the character's speed stat and order them base don that to get a simple turn order going.

    So I want something like this:

    Code (CSharp):
    1. private void TurnOrder()
    2.     {
    3.         foreach (Character character in characters)
    4.         {
    5.             //Where characters is a List<Character>
    6.             //Sort them in the list based on character.Speed
    7.         }
    8.     }
    I can't quite wrap my head around how to do this though. I think I'll need 2 lists, the original characters list and a new list that holds the turn order, but I just don't see how to add them in the right order or re-order them.

    I started looking more into List functions since I'm not very familiar with them but I'm having a hard time understanding what the best option is. Seems like what I need is to create a Comparison of sorts?

    Any help with this would be really great!
     
  2. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    Hi @Nerevar7

    I have no idea about the "best way", but one way is to use C# IComparable:
    https://docs.microsoft.com/en-us/dotnet/api/system.icomparable


    If your characters have speed, then do a comparison based on speed:
    Code (CSharp):
    1. [System.Serializable]
    2. public class Character : IComparable
    3. {
    4.     public string id;
    5.     public int speed;
    6.  
    7.     public int CompareTo(object obj)
    8.     {
    9.         var a = this;
    10.         var b = obj as Character;
    11.      
    12.         if (a.speed < b.speed)
    13.             return -1;
    14.      
    15.         if (a.speed > b.speed)
    16.             return 1;
    17.  
    18.         return 0;
    19.     }
    20. }

    Then in your code, call Sort to sort your List in place:
    Code (CSharp):
    1. characters.Sort();
     
  3. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    @eses

    Hey thanks a lot for the help! It took me a while to understand what was going on with the ComapreTo method but I think I'm getting it now, though I had to switch the greater and less than signs on the if statements since I want the list to be in descending order rather than ascending.

    It seems to be working though! One question regarding this though because I've never used this before:

    What happens if I want to compare some other variable in my Character class? Is it possible to have CompareTo methods that have different variables they are using? Such as strength or a name (for alphabetical sorting)?
     
  4. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    @Nerevar7

    "because I've never used this before..."

    I think you should read more from MS site and other sites... I've pretty much used it only like this, haven't had any other use for it (yet).
     
  5. Errorsatz

    Errorsatz

    Joined:
    Aug 8, 2012
    Posts:
    555
    Another way to do this, and one that's a lot more convenient for being able to use different orderings at different times, is to use LINQ.

    For example, to iterate through them in order of Speed:
    Code (CSharp):
    1. foreach(var character in characters.OrderByDescending(ch => ch.Speed))
    2. {
    3.     ...
    4. }
    If you want to instead sort the list and then keep it sorted, you could do this:
    Code (CSharp):
    1. characters = characters.OrderByDescending(ch => ch.Speed).ToList();

    People will probably be along shortly to mention that LINQ has overhead. This is true, and if you're doing this every Update then profile it and see if there's a bottleneck or GC spikes. I emphasized "profile" there, because the #1 most common optimization mistake is to start changing things based on what "seems" slow without actually testing - a good way to waste a lot of time.
     
  6. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    @Errorsatz

    That's pretty convenient, though I'm not familiar with it either. I ended up setting up an implementation using
    Comparison
    so I can just have a couple of different methods like CompareByName, CompareBySpeed, etc.

    So far it's work great, but I'll do a little research on LINQ, thanks a lot!
     
  7. Rachan

    Rachan

    Joined:
    Dec 3, 2012
    Posts:
    776
    Thank you very much!!!