Search Unity

Need help with List.IndexOf()

Discussion in 'Scripting' started by Ian094, Jan 31, 2015.

  1. Ian094

    Ian094

    Joined:
    Jun 20, 2013
    Posts:
    1,548
    I'm trying to create a ranking system for a racing game. So far I have each car's race completion percentage so I know which one is 1st, 2nd, 3rd etc.

    I have a List of the cars race completion percentages and I've sorted it using List.Sort();

    I also have another List that contains all the racer cars.

    I'm not sure if this is the best way but what I'm trying to do is check whether a racer car's race completion matches with a race completion float in the list, if so I get the indexOf the matching race completion in the list and add 1 to get the cars rank. Apparently, it doesn't work how it's supposed to.

    Here's my code :

    Code (CSharp):
    1.     for(int i = 0; i < myCarList.Count; i++){
    2.            
    3.             if(RaceCompletion[i] == myCarList[i].raceCompletion){            
    4.                     myCarList[i].rank = RaceCompletion.IndexOf(RaceCompletion[i]) + 1;
    5.                 }
    6.             }
    I got a solution from this thread a while ago, but it doesn't seem to always be correct.

    I'd really appreciate any help. Thanks
     
  2. fire7side

    fire7side

    Joined:
    Oct 15, 2012
    Posts:
    1,819
    I can't see all your code, but comparing floats to be equal can cause problems because floats give slightly different results in different situations. If you are going to compare them, it might be better to round them off and use ints if possible.
     
  3. Ian094

    Ian094

    Joined:
    Jun 20, 2013
    Posts:
    1,548
    Oh yeah, I didn't think of it that way. I'll try using rounding the floats to int and see whether it works. Thanks for the suggestion.

    Here's my full code :
    Code (CSharp):
    1. public Car_Stats[] carList;
    2.  
    3. public List<Car_Stats> myCarList = new List<Car_Stats>();
    4.    
    5. public List<float> RaceCompletion = new List<float>();
    6.  
    7. void Start () {
    8.        
    9.         carList = GameObject.FindObjectsOfType(typeof(Car_Stats)) as Car_Stats[];  
    10.        
    11.         for(int i = 0; i < carList.Length; i++){
    12.             myCarList.Add(carList[i]);
    13.             RaceCompletion.Add(carList[i].raceCompletion);
    14.         }
    15. }
    16.  
    17. //Called every frame
    18. void SetRank(){
    19.            
    20.         for(int i = 0; i < myCarList.Count; i++){
    21.            
    22.             RaceCompletion[i] = myCarList[i].raceCompletion;
    23.            
    24. //Sort from largest to smallest
    25.             RaceCompletion.Sort();
    26.             RaceCompletion.Reverse();
    27.            
    28.             if(RaceCompletion[i] == myCarList[i].raceCompletion){
    29.                                                
    30.                     myCarList[i].rank = RaceCompletion.IndexOf(RaceCompletion[i]) + 1;
    31.                 }
    32.             }
    33.         }    
     
  4. Ian094

    Ian094

    Joined:
    Jun 20, 2013
    Posts:
    1,548
    @fire7side your suggestion works better than using floats. I'm getting much more accurate ranks but using ints is not always so accurate though, for example when the race begins, all the cars percentages are rounded to 0 so car ranks are mixed up and also sometimes during the race the ranks aren't set correctly.

    I guess my approach to figuring this out wasn't the best one. I tried Using List.Insert() but it kept adding to the list endlessly so I gave up on that approach.
     
  5. fire7side

    fire7side

    Joined:
    Oct 15, 2012
    Posts:
    1,819
    One thing you can do is multiply by 10 or 100 and then round it off. Since you are comparing, it doesn't matter because the relative size is the same, but it gives the equivalent of 2 digit decimal precision. Also, use greater than or less than for comparison, rather than equals. If it's greater than one place and less than another place, then it's equal. That way you could use floats.
     
    Last edited: Jan 31, 2015
  6. Ian094

    Ian094

    Joined:
    Jun 20, 2013
    Posts:
    1,548
    I've rounded the floats to 1 decimal place and set a Debug.Log to let me know what the script returns.

    Everything seems to be working great because it logs the cars with their correct race completion percentage but it's getting the indexOf() completely wrong. I don't know what the problem could be. Any help would really be appreciated.

    Code (CSharp):
    1.     void SetRank(){
    2.        
    3.         for(int i = 0; i < myCarList.Count; i++){
    4.            
    5.             RaceCompletion[i] = myCarList[i].raceCompletion;
    6.            
    7.             if(RaceCompletion[i] == myCarList[i].raceCompletion){
    8.                                
    9.                 Debug.Log(myCarList[i].name + " : " + myCarList[i].raceCompletion + " : " + RaceCompletion.IndexOf(RaceCompletion[i]));
    10.                
    11.                 myCarList[i].rank = RaceCompletion.IndexOf(RaceCompletion[i]) + 1;
    12.                
    13.             }
    14.         }
    15.        
    16.        
    17.         RaceCompletion.Sort();
    18.         RaceCompletion.Reverse();
    19.     }    
     
  7. Ian094

    Ian094

    Joined:
    Jun 20, 2013
    Posts:
    1,548
    The values clearly don't match when it tries to get the indexOf the race completion :

    Untitled.png
     
  8. Ian094

    Ian094

    Joined:
    Jun 20, 2013
    Posts:
    1,548
    Bump.
     
  9. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Instead of using two arrays and trying to keep both in sync why don't you create a class that contains a racer and his completion percentage and maintain an array/list of that class?
     
  10. fire7side

    fire7side

    Joined:
    Oct 15, 2012
    Posts:
    1,819
    My guess is you are using the decimal in your int comparison and it is giving you the wrong results. The ints have to be left 10 times larger during the comparison, and only after the comparison can you divide by 10 and get the real results. Using a class would make sense, but then you would need to sort the array by one field, which is a little more complicated.

    The other way I mentioned isn't difficult leaving it a float:
    for(int i = 0; i<arr.size;++i){
    if(myTestNumber < arr[i+1] && myTestNumber > arr[i-1]{
    // myNumber is equal to this place
    }
    }

    The other thing you can do is leave it as a float, but only check to the first decimal point, which isn't very hard either.
     
    Last edited: Feb 2, 2015
  11. Ian094

    Ian094

    Joined:
    Jun 20, 2013
    Posts:
    1,548
    @KelsoMRK your idea sounds great. I just created a class within my "Rank_Mangaer" class containing a racer car & it's race completion :
    Code (CSharp):
    1. public class Rank_Manager : MonoBehaviour {
    2.  
    3.     [System.Serializable]
    4.     public class CarRank{
    5.         public float completionPercentage;
    6.         public GameObject car;
    7.     }
    8.  
    9. public Car_Stats[] carList;
    10. public List<CarRank> carRank = new List<CarRank>();
    11.  
    12. void Start () {
    13.            
    14.         carList = GameObject.FindObjectsOfType(typeof(Car_Stats)) as Car_Stats[];
    15.    
    16. }
    17.  
    18. //Called every frame
    19.     void Rank(){
    20.    
    21.    
    22.         for(int i = 0; i < carRank.Count; i++){
    23.        
    24.             carRank[i].completionPercentage = carList[i].raceCompletion;
    25.             carRank[i].car = carList[i].gameObject;
    26.        
    27.         }
    28.    
    29.         carCompletion.Sort();
    30.         carRank.Reverse();
    31.    
    32.         SetCarRank();
    33.     }
    34.  
    35.  
    36.     void SetCarRank(){
    37.         for(int r = 0; r < carRank.Count; r++){
    38.             carRank[r].car.GetComponent<Car_Stats>().rank = r + 1;
    39.         }
    40.     }
    The only problem is I can't manage to sort the list according to the "completionPercentage" float in each element without getting errors. I can't seem to set the length of the list on Start() either so I do it manually in the inspector


    @fire7side It doesn't accurately set the ranks. For example If I have the highest race completion percentage, sometimes it shows my rank as 4th and other times as 1st...I'm not sure what I'm doing wrong.

    Thanks for your suggestions.
     
    Last edited: Feb 3, 2015
  12. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    Just thought I should explain:

    This makes perfect sense, as you're first printing out the list, and then sorting and reversing it. So in the debug, you're seeing the results from the list as it was, while in the inspector, the list has been sorted.
     
  13. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
  14. Ian094

    Ian094

    Joined:
    Jun 20, 2013
    Posts:
    1,548
    Ok, I've finally got it working. I had to implement the IComparer<> interface. It took a while to figure things out but it finally works flawlessly. Thanks for the suggestion @KelsoMRK.

    Here's the code i used In-case anyone comes across the same issue :

    Code (CSharp):
    1. public class Rank_Manager : MonoBehaviour {
    2.  
    3.     [System.Serializable]
    4.     public class CarRank : IComparer<CarRank>{
    5.         public float completionPercentage;
    6.         public GameObject car;
    7.    
    8.         public int Compare(CarRank x, CarRank y){
    9.        
    10.             return x.completionPercentage.CompareTo(y.completionPercentage);
    11.        
    12.         }
    13.     }
    14.  
    15.     [HideInInspector]
    16.     public Car_Stats[] carList;
    17.  
    18.     public List<CarRank> carRank = new List<CarRank>();
    19.  
    20. void Start () {
    21.         carList = GameObject.FindObjectsOfType(typeof(Car_Stats)) as Car_Stats[];
    22.       }  
    23. }
    24.  
    25. //Called every frame
    26. void Rank(){
    27.    
    28.    
    29.         for(int i = 0; i < carRank.Count; i++){
    30.        
    31.             carRank[i].completionPercentage = carList[i].raceCompletion;
    32.             carRank[i].car = carList[i].gameObject;
    33.        
    34.         }
    35.    
    36.    
    37.     CarRank carRanker = new CarRank();
    38.     carRank.Sort(carRanker);
    39.     carRank.Reverse();
    40.  
    41.     SetCarRank();
    42.     }
    43.  
    44.  
    45. void SetCarRank(){
    46.         for(int r = 0; r < carRank.Count; r++){
    47.             carRank[r].car.GetComponent<Car_Stats>().rank = r + 1;
    48.         }
    49.     }
     
    Last edited: Feb 4, 2015