- Home /
sort transforms by distance to player
I'm following a tutorial and trying to convert it to unity javascript : BurgZergArcade
but I cannot work out how to do the : Sort - delegate - CompareTo
I found an example (I think the person was doing the same tutorial) : http://forum.unity3d.com/threads/68805-Sort-and-delegate-working-together
and another example : http://answers.unity3d.com/questions/22261/sorting-builtin-arrays.html
but these have left me more confused, e.g. in the first example link, the person helping has created a new function called Sort.
This is what is supposed to happen :
The ArrayList targets is populated by all enemies on stage (found with tag)
the function SortTargetsByDistance() is supposed to Sort the ArrayList so that targets[0] is the closest, targets[2'] is the furthest away
in C# :
targets.Sort(delegate(Transform t1, Transform t2)
{
return Vector3.Distance(t1.position, myTransform.position)
.CompareTo(Vector3.Distance(t2.position, myTransform.position));
}
);
Any help would be greatly appreciated. Here's my script so far :
#pragma strict
public class PlayerTargetting extends MonoBehaviour
{
public var targets = new ArrayList();
public var selectedTarget : Transform;
private var myTransform : Transform;
function Start() : void
{
selectedTarget = null;
myTransform = transform;
FindAllEnemies();
}
function Update() : void
{
if (Input.GetKeyDown(KeyCode.Tab))
TargetEnemy();
}
public function FindAllEnemies() : void
{
var go : GameObject[] = GameObject.FindGameObjectsWithTag("Enemy");
for (var enemy : GameObject in go)
AddTarget(enemy.transform);
// debug to show array before sorting
for (var e : int = 0; e < targets.Count; e++)
Debug.Log("targets["+e+"] = " + targets[e]);
}
public function AddTarget(enemy : Transform) : void
{
targets.Add(enemy);
}
function TargetEnemy() : void
{
if (selectedTarget == null)
{
SortTargetsByDistance();
selectedTarget = targets[0];
}
}
function SortTargetsByDistance() : void
{
/*
// C#
targets.Sort(delegate(Transform t1, Transform t2)
{
return Vector3.Distance(t1.position, myTransform.position)
.CompareTo(Vector3.Distance(t2.position, myTransform.position));
}
);
*/
// ----
/*
// JS example from forum 'site
function Sort(t1 : Transform, t2 : Transform){
return Vector3.Distance(t1.position, myTransform.position)
>(Vector3.Distance(t2.position, myTransform.position));
}
print( targets.Sort( Vector3(0,0,0), Vector3(0,1,0) ) );
*/
// ----
targets.Sort(); // ????
// debug to show array after sorting
for (var e : int = 0; e < targets.Count; e++)
Debug.Log("targets["+e+"] = " + targets[e]);
}
}
What's wrong with that code ? The Sort without parameter ? I'm not 100% sure, but I think this one will use the object's hashcode to sort the array. Transform in your case, so it's gonna be random I guess. But the delegate seems fine to me, even if you should use the sqr$$anonymous$$agnitude ins$$anonymous$$d of the magnitude.
When SortTargetsByDistance is called , I get the following error (as below).
If I do a basic conversion :
targets.Sort(delegate(t1 : Transform, t2 : Transform)
{
return Vector3.Distance(t1.position, myTransform.position)
.CompareTo(Vector3.Distance(t2.position, myTransform.position));
}
);
I get ; BCE0043: Unexpected token: t1. ; BCE0044: expecting ), found '{'. ; etc showing the syntax delegate is wrong (and i cannot find it int the Unity Scripting Ref)
targets.Sort(); error :
InvalidOperationException: No IComparable or IComparable interface found. System.Array.compare[Object] (System.Object value1, System.Object value2, IComparer`1 comparer) System.Array.qsort[Object,Object] (System.Object[] keys, System.Object[] items, Int32 low0, Int32 high0, IComparer`1 comparer) System.Array.Sort[Object,Object] (System.Object[] keys, System.Object[] items, Int32 index, Int32 length, IComparer`1 comparer) Rethrow as InvalidOperationException: The comparer threw an exception.
I guess I have to write a sorting function if there is no similar command in uJS. Thanks =]
Here is an example of how I sort a list of custom gui class by their depth (never$$anonymous$$d why, I sort objects by an int to say it otherwise) :
public List[IGUI] guis; // The character for generics won't show, so it's replaced by [] here // ... guis.Sort( CompareGUIByDepth ); // ... // Trie une liste en ordre décroissant de profondeur private static int CompareGUIByDepth(IGUI g1, IGUI g2) { if (g1 == null) { // If g1 is null and g2 is null, they're equal. if (g2 == null) return 0; // If g1 is null and g2 is not null, g2 is greater. else return 1; } else // If g1 is not null... { // ...and g2 is null, g1 is greater. if (g2 == null) return -1; // ...and g2 is not null, compare the depth of the two iGUI. else return g1.depth == g2.depth ? 0 : ( g1.depth > g2.depth ? -1 : 1); } }
You might want to use CompareTo ins$$anonymous$$d of my ? :
thanks for the advice and code. (I am not worrying about why! =] am just very happy for the example to look at and understand), but I have to leave it for now until tomorrow (it is late and i should be asleep!), so I shall let you know my results then.
Answer by Fattie · Feb 09, 2016 at 04:35 PM
If using System.Linq;
hits = hits.OrderBy(
x => Vector2.Distance(this.transform.position,x.transform.position)
).ToList();
Linqless
hits.Sort(delegate(Enemy a, Enemy b)
{return Vector2.Distance(this.transform.position,a.transform.position)
.CompareTo(
Vector2.Distance(this.transform.position,b.transform.position) );
});
Hope it saves some typing.
Answer by AlucardJay · Jan 23, 2013 at 09:14 AM
To answer and close my own question : What I coded in the end was a simple bubble-sort loop. This means that from the first item in the list, 2 adjacent variables in the list are compared. The lowest value is stored in the lowestindex of the 2. Then the cycle is started again from the start of the loop. Eventually all the low values bubble* up to the top (like bubbles in a liquid), then when the end of the list is finally reached all the values are sorted in correct ascending order. Here is a code snippet :
function SortTargetsByDistance() : void
{
// bubble-sort transforms
for ( var e : int = 0; e < targets.Count - 1; e ++ )
{
var sqrMag1 : float = ( targets[e + 0].position - myTransform.position ).sqrMagnitude;
var sqrMag2 : float = ( targets[e + 1].position - myTransform.position ).sqrMagnitude;
if ( sqrMag2 < sqrMag1 )
{
var tempStore : Transform = targets[e];
targets[e] = targets[e + 1];
targets[e + 1] = tempStore;
e = 0;
}
}
}
This really helps and make sense .. if you following the tutorial u need just copy, pase and think about it :)
Answer by gooongalooo · Jun 15, 2013 at 08:01 PM
My solution is very similar:
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class PlayerTargetting : MonoBehaviour
{
//Unit is a MonoBehaviour attached to EVERY unit
//Player.cs inherits from Unit.cs
public Player Player;
public List<Unit> NearByEnemies = new List<Unit>();
// Use this for initialization
void Start()
{
Player = GetComponent();
}
/// <summary>
/// Get ClosestEnemy
/// </summary>
/// <returns></returns>
internal Unit ClosestEnemy()
{
SortTargetsByDistance();
return NearByEnemies.First();
}
/// <summary>
/// Sort Targets By Distance
/// </summary>
public void SortTargetsByDistance()
{
// TODO get form a dict <id, Unit> - later from Units in CurrentPlayerArea
NearByEnemies = Managers.SpawnManager.UnitsInWorld.Values.ToList();
NearByEnemies .Sort(
(unit1, unit2) =>
(Player.UnitTransform.position - unit1.UnitTransform.position).sqrMagnitude.CompareTo(
(Player.UnitTransform.position - unit2.UnitTransform.position).sqrMagnitude)
);
}
}
And so on... switching between Units is done by using an indexer... nothing special.
The tutorial comes with a C# method, I need a uJS solution.
Thanks. That List.Sort() syntax was just what I was looking for.
Answer by lacost · May 01, 2012 at 12:55 PM
Sorry for off topic. "sort transforms by distance to player" - it seems like very bad idea, why do you need it for?
P.S You can use any sort algorithm, sort key is Vector3.Distance
I am finding all the enemies on the stage , and storing their transforms in an array called targets . The transforms are like handles where I can access further components, but for this the transform is simply used for it's position.
Why would I want to do this? well , I have a targeting system , and pressing Tab cycles through the targets. What I want is to arrange the enemies in targets so they are in sequential order - from closest to furthest.
In summary this is handy for say my targetting weapon has a set range , what's the point of cycling to the next target if it is the furthest away , then half-way , then closest , then second furthest , then second middle , etc etc etc. The player would find it really annoying if a mob was attacking and the next target wasn't the closest / second closest , etc.
Could you elaborate on 'P.S You can use any sort algorithm, sort key is Vector3.Distance' by any chance ? THIS IS THE WHOLE POINT OF $$anonymous$$Y QUESTION (see the C# example in my question!)
1) Why would I want to do this? - big pice of work. If you have a lot targets (player mooving, targets moving, you need to sort array a lot of times and after few seconds it needs to sort again to keep it up to date. That is bad solution unless you nedd to show all of them to player in one moment) . If you need just choose next neares target when you presing tab, just searcch for appropriate (nearest but not in previous targets array) target every tab click.
2) I do not understand, you whant sort algorithm? Here it is on of them.
But You need to deside by your self with algorithm is good for you task. There is no best sort algorithm etc.. choose your from here http://en.wikipedia.org/wiki/Sorting_algorithm
Array A, contains N objects,from A[1] to A[N]
t = true whilet t = true t = false for j = 1, 2, ..., n − 1: if A[j] > A[j+1], than: switch A[j] to A[j+1] t = true
exep A[j] > A[j+1] use Vector3.Distance(t1.position, myTransform.position) > Vector3.Distance(t2.position, myTransform.position);
While I appreciate the comments, they are really not getting me far ...
1) the array is updated by the player pressing the 'Tab' button , so that's not an issue really.
2) I figured out I would probably need a bubble-sort to arrange my distances, and this seems to be accomplished easily in C#
So I guess my question would be : How do I convert This script to C# :
targets.Sort(delegate(Transform t1, Transform t2)
{
return Vector3.Distance(t1.position, myTransform.position)
.CompareTo(Vector3.Distance(t2.position, myTransform.position));
}
);
considering there doesn't seem to be any Unity JavaScript equivalent to Sort(delegate ... .CompareTo ....
thanks for the link though =]
Your answer
Follow this Question
Related Questions
Sort an ArrayList of game objects by another script variable 2 Answers
Is it possible to sort an array determined by an external comparison? 1 Answer
How to alphabetically sort your List? 2 Answers
Sorting array 0 Answers
How to get an array of the positions of a sorted array without changing its positions. 1 Answer