- Home /
Problem with using IComparer
I'm trying to use a comparer that sorts a List of Vector2 points by their clockwise position from an origin. I got the comparer code from here https://pastebin.com/1RkaP28U#, and its class is called ClockwiseComparer. Due to the Hydra.HydraCommon.Utils.Comparers line, I figured I would have to add
using Hydra.HydraCommon.Utils.Comparers;
to the script that I use the Comparer in, which is called Player. So, I create the ClockwiseComparer comparer
object within Player, and set it to new ClockwiseComparer(origin)
However, when I try to use it at list.Sort(comparer), the compiler gives this complaint.
Cannot convert from Hydra.HydraCommon.Utils.Comparers to System.Collections.Generic.IComparer<UnityEngine.GameObject>
That was the original problem. Afterward I tried to solve it by making the ClockwiseComparer class into something more resembling System.Collections.Generic.IComparer<UnityEngine.GameObject>
. I changed the namespace to namespace System.Collections.Generic.IComparer
, and likewise added using System.Collections.Generic.IComparer;
to the Player class. This simply resulted in
cannot convert from System.Collections.Generic.IComparer.ClockwiseComparer to System.Collections.Generic.IComparer<UnityEngine.GameObject>
Here are both the Player and CounterClockwise scripts from the latter case.
using System.Collections.Generic.IComparer;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour {
[SerializeField] float moveSpeed = 10f;
[SerializeField] float padding = 1f;
[SerializeField] GameObject projectile;
[SerializeField] GameObject flag;
[SerializeField] float projectileSpeed =10f;
ClockwiseComparer comp;
Vector2 origin;
float xMin;
float xMax;
float yMin;
float yMax;
MeshFilter filter;
List<GameObject> flags = new List<GameObject>();
// Use this for initialization
void Start () {
SetUpMoveBoundaries();
StartCoroutine(delayedLog());
InitializeBaseMesh();
}
// Update is called once per frame
void Update () {
FireLaser();
PlaceFlag();
}
private void FindFlagOrigin()
{
float totalX = 0;
float totalY = 0;
foreach (GameObject flag in flags)
{
totalX += flag.transform.position.x;
totalY += flag.transform.position.y;
}
float centerX = totalX / flags.Count;
float centerY = totalY / flags.Count;
origin = new Vector2(centerX, centerY);
if (comp == null)
comp = new ClockwiseComparer(origin);
}
private void ReorderFlags()
{
flags.Sort(comp);
/*for (int i=0; i<flags.Count-1; i++)
{
int closestFlagIndex = GetClosestFlag(flags[i], Extensions.GetRange(flags, i+1));
//switches the nearest flag and the flag that is next in sequence to the current flag
GameObject flagHolder = flags[i + 1];
flags[i + 1] = flags[closestFlagIndex];
flags[closestFlagIndex] = flagHolder;
}*/
}
}
using System.Collections.Generic;
using System.Collections.Generic;
using UnityEngine;
namespace System.Collections.Generic.IComparer
{
public class ClockwiseComparer : IComparer<Vector2>
{
private Vector2 m_Origin;
#region Properties
/// <summary>
/// Gets or sets the origin.
/// </summary>
/// <value>The origin.</value>
public Vector2 origin { get { return m_Origin; } set { m_Origin = value; } }
#endregion
/// <summary>
/// Initializes a new instance of the ClockwiseComparer class.
/// </summary>
/// <param name="origin">Origin.</param>
public ClockwiseComparer(Vector2 origin)
{
m_Origin = origin;
}
#region IComparer Methods
/// <summary>
/// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other.
/// </summary>
/// <param name="first">First.</param>
/// <param name="second">Second.</param>
public int Compare(Vector2 first, Vector2 second)
{
return IsClockwise(first, second, m_Origin);
}
#endregion
/// <summary>
/// Returns 1 if first comes before second in clockwise order.
/// Returns -1 if second comes before first.
/// Returns 0 if the points are identical.
/// </summary>
/// <param name="first">First.</param>
/// <param name="second">Second.</param>
/// <param name="origin">Origin.</param>
public static int IsClockwise(Vector2 first, Vector2 second, Vector2 origin)
{
if (first == second)
return 0;
Vector2 firstOffset = first - origin;
Vector2 secondOffset = second - origin;
float angle1 = Mathf.Atan2(firstOffset.x, firstOffset.y);
float angle2 = Mathf.Atan2(secondOffset.x, secondOffset.y);
if (angle1 < angle2)
return -1;
if (angle1 > angle2)
return 1;
// Check to see which point is closest
return (firstOffset.sqrMagnitude < secondOffset.sqrMagnitude) ? -1 : 1;
}
}
}
Thank you for reading.
Answer by Bunny83 · Oct 22, 2018 at 05:31 PM
Your collection (your flags list) is a collection of GameObjects (since it's a List<GameObject>
). Your comparer however compares Vector3s and not GameObject so you can not use this compare to compare GameObjects
You need a Comparer that implements IComparer<GameObject>
in order to sort a list of gameobjects.
If you need to deal a lot with the positions of the gameobjects it's better to actually use a List<Transform>
and store the transform components of your objects instead of their gameobjects. In this case your comparer needs to implement IComparer<Transform>
of course. Though that way the compare doesn't need to access the .transform property which internally calls GetComponent.
Thank you. I've attempted to modify the code as such,
using System.Collections.Generic;
using UnityEngine;
namespace System.Collections.Generic.IComparer
{
public class ClockwiseComparer : IComparer<GameObject>
{
private Vector2 m_Origin;
#region Properties
/// <summary>
/// Gets or sets the origin.
/// </summary>
/// <value>The origin.</value>
public Vector2 origin { get { return m_Origin; } set { m_Origin = value; } }
#endregion
/// <summary>
/// Initializes a new instance of the ClockwiseComparer class.
/// </summary>
/// <param name="origin">Origin.</param>
public ClockwiseComparer(Vector2 origin)
{
m_Origin = origin;
}
#region IComparer $$anonymous$$ethods
/// <summary>
/// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other.
/// </summary>
/// <param name="first">First.</param>
/// <param name="second">Second.</param>
public int Compare(GameObject first, GameObject second)
{
return IsClockwise(first, second, m_Origin);
}
#endregion
/// <summary>
/// Returns 1 if first comes before second in clockwise order.
/// Returns -1 if second comes before first.
/// Returns 0 if the points are identical.
/// </summary>
/// <param name="first">First.</param>
/// <param name="second">Second.</param>
/// <param name="origin">Origin.</param>
public static int IsClockwise(GameObject first, GameObject second, Vector2 origin)
{
if (first == second)
return 0;
Vector2 firstOffset = (Vector2)first.transform.position - origin;
Vector2 secondOffset = (Vector2)second.transform.position - origin;
float angle1 = $$anonymous$$athf.Atan2(firstOffset.x, firstOffset.y);
float angle2 = $$anonymous$$athf.Atan2(secondOffset.x, secondOffset.y);
if (angle1 < angle2)
return -1;
if (angle1 > angle2)
return 1;
// Check to see which point is closest
return (firstOffset.sqr$$anonymous$$agnitude < secondOffset.sqr$$anonymous$$agnitude) ? -1 : 1;
}
}
}
but now it's throwing an InvalidOperationException. No IComparable or IComparable interface found.
Are you sure you still using
flags.Sort(comp);
? The error you mentioned would only be thrown if you don't specify a custom comparer. In this case the items of the list have to implement the IComparable interface. If it doesn't this error is thrown. So make sure you don't just use flags.Sort();
and that "comp" is not null (i.e. actually initialized). From your code it's not clear when you actually call "FindFlagOrigin". Since you initialize comp in this method if you did not call it before Update is called comp will not be initialized. Wouldn't it make more sense to initialize it either in Start or if you want to keep it lazy initialized, initialize it right before you use it.
D'oh. That was exactly it. I had the comp initialized in a separate method that I forgot to call. Thank you, my code is working fine now
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
Sorting Collider Arrays by location of elements 1 Answer
How to sort get components? 3 Answers
Can't use Namespaces with Mono and 1 class 1 namespace file 1 Answer