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): for(int i = 0; i < myCarList.Count; i++){ if(RaceCompletion[i] == myCarList[i].raceCompletion){ myCarList[i].rank = RaceCompletion.IndexOf(RaceCompletion[i]) + 1; } } 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
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.
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): public Car_Stats[] carList; public List<Car_Stats> myCarList = new List<Car_Stats>(); public List<float> RaceCompletion = new List<float>(); void Start () { carList = GameObject.FindObjectsOfType(typeof(Car_Stats)) as Car_Stats[]; for(int i = 0; i < carList.Length; i++){ myCarList.Add(carList[i]); RaceCompletion.Add(carList[i].raceCompletion); } } //Called every frame void SetRank(){ for(int i = 0; i < myCarList.Count; i++){ RaceCompletion[i] = myCarList[i].raceCompletion; //Sort from largest to smallest RaceCompletion.Sort(); RaceCompletion.Reverse(); if(RaceCompletion[i] == myCarList[i].raceCompletion){ myCarList[i].rank = RaceCompletion.IndexOf(RaceCompletion[i]) + 1; } } }
@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.
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.
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): void SetRank(){ for(int i = 0; i < myCarList.Count; i++){ RaceCompletion[i] = myCarList[i].raceCompletion; if(RaceCompletion[i] == myCarList[i].raceCompletion){ Debug.Log(myCarList[i].name + " : " + myCarList[i].raceCompletion + " : " + RaceCompletion.IndexOf(RaceCompletion[i])); myCarList[i].rank = RaceCompletion.IndexOf(RaceCompletion[i]) + 1; } } RaceCompletion.Sort(); RaceCompletion.Reverse(); }
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?
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.
@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): public class Rank_Manager : MonoBehaviour { [System.Serializable] public class CarRank{ public float completionPercentage; public GameObject car; } public Car_Stats[] carList; public List<CarRank> carRank = new List<CarRank>(); void Start () { carList = GameObject.FindObjectsOfType(typeof(Car_Stats)) as Car_Stats[]; } //Called every frame void Rank(){ for(int i = 0; i < carRank.Count; i++){ carRank[i].completionPercentage = carList[i].raceCompletion; carRank[i].car = carList[i].gameObject; } carCompletion.Sort(); carRank.Reverse(); SetCarRank(); } void SetCarRank(){ for(int r = 0; r < carRank.Count; r++){ carRank[r].car.GetComponent<Car_Stats>().rank = r + 1; } } 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.
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.
You can solve the sorting issue by making your class implement the IComparable<T> interface: https://msdn.microsoft.com/en-us/library/4d7sx9hd(v=vs.110).aspx
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): public class Rank_Manager : MonoBehaviour { [System.Serializable] public class CarRank : IComparer<CarRank>{ public float completionPercentage; public GameObject car; public int Compare(CarRank x, CarRank y){ return x.completionPercentage.CompareTo(y.completionPercentage); } } [HideInInspector] public Car_Stats[] carList; public List<CarRank> carRank = new List<CarRank>(); void Start () { carList = GameObject.FindObjectsOfType(typeof(Car_Stats)) as Car_Stats[]; } } //Called every frame void Rank(){ for(int i = 0; i < carRank.Count; i++){ carRank[i].completionPercentage = carList[i].raceCompletion; carRank[i].car = carList[i].gameObject; } CarRank carRanker = new CarRank(); carRank.Sort(carRanker); carRank.Reverse(); SetCarRank(); } void SetCarRank(){ for(int r = 0; r < carRank.Count; r++){ carRank[r].car.GetComponent<Car_Stats>().rank = r + 1; } }