Problem using IComparer to sort list of GameObjects

Hello everyone,

I’ve been trying to understand how to sort a list of GameObjects by a custom attribute, but without success so far. I must be doing something wrong.
I’m trying to do this for a* pathfinding. I have a list of tiles, each with F, G and H scores. Now I want to sort them by F score, so, in my class TileStatus attached to the Tile GameObject I have this :

using UnityEngine;
using System;
using System.Collections;


public class TileStatus : MonoBehaviour,IComparable {
	
	public double F;
	public double G;
	public double H;
	public GameObject parent = null;
	
	 int IComparable.CompareTo ( object obj)
	{
		TileStatus tile = ( TileStatus )obj;
		if ( this . getFScore()< tile . getFScore() )
		{
		return -1;
		}
		if ( this . getFScore() > tile . getFScore() )
		{
			return 1;
		}
		else
		{
		return 0;
		}
	}
	
	public void setParent(GameObject parentTile)
	{
		parent = parentTile;
	}
	
	public GameObject getParent()
		
	{
		return parent;	
	}
	
	public double getFScore ()
	{
		return F;	
	}
	
	public void setFScore (double x)
	{
		F = x;	
	}
	
	public double getHScore()
	{
		return H;
	}
	
		public double getGScore()
	{
		return G;
	}
	
	public void setGScore (double x)
	{
		G = x;	
	}
	
	public void setHScore (double x)
	{
		H = x;	
	}
		
	}	
	void Start () {
	
		if (gameObject.tag == "Free square")
		{
			isOccupied = false;
		}
		else 
		{
			isOccupied = true;
		}
		
		setFScore(0);
		setGScore(0);
		setHScore(0);
		
	}
	
	// Update is called once per frame
	void Update () {
	
	}
	
	
	
	
}

but for some reason when it comes to the sorting itself Unity sends me this error :

“ArgumentException: does not implement right interface
System.Collections.Generic.Comparer1+DefaultComparer[UnityEngine.GameObject].Compare (UnityEngine.GameObject x, UnityEngine.GameObject y) (at /Applications/buildAgent/work/b59ae78cff80e584/mcs/class/corlib/System.Collections.Generic/Comparer.cs:86) System.Array.compare[GameObject] (UnityEngine.GameObject value1, UnityEngine.GameObject value2, IComparer1 comparer) (at /Applications/buildAgent/work/b59ae78cff80e584/mcs/class/corlib/System/Array.cs:1744)
System.Array.qsort[GameObject,GameObject] (UnityEngine.GameObject keys, UnityEngine.GameObject items, Int32 low0, Int32 high0, IComparer1 comparer) (at /Applications/buildAgent/work/b59ae78cff80e584/mcs/class/corlib/System/Array.cs:1721) System.Array.Sort[GameObject,GameObject] (UnityEngine.GameObject[] keys, UnityEngine.GameObject[] items, Int32 index, Int32 length, IComparer1 comparer) (at /Applications/buildAgent/work/b59ae78cff80e584/mcs/class/corlib/System/Array.cs:1674)
Rethrow as InvalidOperationException: The comparer threw an exception.
System.Array.Sort[GameObject,GameObject] (UnityEngine.GameObject keys, UnityEngine.GameObject items, Int32 index, Int32 length, IComparer1 comparer) (at /Applications/buildAgent/work/b59ae78cff80e584/mcs/class/corlib/System/Array.cs:1677) System.Array.Sort[GameObject] (UnityEngine.GameObject[] array, Int32 index, Int32 length, IComparer1 comparer) (at /Applications/buildAgent/work/b59ae78cff80e584/mcs/class/corlib/System/Array.cs:1623)
System.Collections.Generic.List`1[UnityEngine.GameObject].Sort () (at /Applications/buildAgent/work/b59ae78cff80e584/mcs/class/corlib/System.Collections.Generic/List.cs:568)”

I’d be very happy if anyone could help me out because I don’t see what’s wrong here.
Thanks !!

Unfortunately I first tried with public int CompareTo and it gav me the same error. It is trying to use IComparable because it's the way you can define a "custom" sort for lists in c#, although I do not master that aspect, and that's probably where my error is, but i don't get where even if I've read dozens of threads about IComparable.

2 Answers

2

I think you want to implement an object using the IComparer interface rather than IComparable. However, normally you can just do the sort using the lambda version of Sort:

E.g:

  children.Sort((c1, c2) => (int) (c1.topLeft.x - c2.topLeft.x));

Thanks for the answer, however I need to access the "F" attribute from each of both gameobjects. So correct me if I'm wrong, but i need to use the function getFScore don't I ? Otherwise Unity won't know what "F" refers to. Again, correct me if I'm wrong, but I need two separate instances of the script (one for each tile?) and I don't really know how I could do it, that's why I've set up the CompareTo directly in the script attached to the tiles. Do you have any advice on how I should proceed instead? Thanks again.

Sure you can do that - if it's an array of GameObject you can do this: children.Sort((c1,c2)=> (int) (c1.GetComponent<TileStatus>().getFScore() - c2.GetComponent<TileStatus>().getFScore()));

Yes, I'm sorry I forgot to come back but I solved my problem by writing a comparison function as you wrote here. I feel silly not to have thought about it earlier :). I didn't find the problem with the IComparable interface, however I don't really need it. Thanks for your help though :) Is there a way to vote to earn helpers any kind of reward?

I use IComparer all the time as well - but it's definitely IComparer you need for that sort, not IComparable...

@laoril - only when you have 15 karma (which you do now because I upvoted your question - seemed like a sensible one to me :) You can upvote comments (but that awards no karma, just indicates the ones that are relevant). You can upvote answers (15 karma) and you can tick answers. You can accept an answer (20 karma) and you can post your own answer and tick that (no karma for that I'm afraid) if you feel you did something different.

I ran into the same issue with IComparable - Here are the workarounds.