0

I'm trying to find the nth nearest object to the player and have looked around and come to the conclusion that a 2D ArrayList or List seems to be what I need to store the Object id's and their distances to the player, then sorting it by ascending order. However i'm not sure how to achieve this with Lists. I do currently have working code to find the nearest object but finding the nth seems to be much trickier without using a LOT of variables.

This question below was the closest answer I've seen - however they use two Strings and not two different values such as an Object and an int which I need.

How to Sort 2D ArrayList<String> by Only the First Element

List<ArrayList<GameObject>> nearest = new ArrayList<ArrayList<GameObject>>();

nearest.add(new ArrayList<GameObject>(Arrays.asList(instance, int))); //Adds a new instance and it's distance to the player.

Currently i'm getting an error where i'm not allowed both an object and int in the array and I can't seem to define it for both types.

OutOfMind
  • 874
  • 16
  • 32
  • It's very unclear why you would need a 2D list. You have objects. You want the nth nearest. So you can just sort the list of objects by their distance to the player, and take the nth element in this sorted list. If your GemeObject doesn't have a distance field, and computing the distance is costly, then compute the distance f each object, and wrap the object and its distance into another object (GameObjectWithDistance), then sort that list of GameObjectWithDistance. – JB Nizet Aug 18 '17 at 07:21
  • My list of objects only contains the objects, i'm looking for a way to store the distances temporarily in the list as well and sort by ascending order while it matches up to it's equivalent object. – Fluidic Ice Aug 18 '17 at 07:29
  • 2
    As I said, create a class wrapping your object and its distance (i.e. GameObjectWithDistance, having two fields gameObject, and distance), create a list of those objects, and sort that list. Or just compute the distance of each GameObject each time you need to compare it with the distance of another GameObject if computing the distance is cheap. – JB Nizet Aug 18 '17 at 07:31
  • I don't quite understand what you mean sorry JB, it would be good if you could post a solution with code examples. This is my current method for finding the nth Object: https://pastebin.com/hiB39QzF – Fluidic Ice Aug 18 '17 at 08:59

2 Answers2

2

Let's just say that you have a collection (Set, List, whatever) of GameObject:

Collection<GameObject> gameObjects = ...;

You also have, somewhere, a method used to compute the distance of one GameObject to the player. I'll assume it returns an int:

public int computeDistanceToPlayer(GameObject gameObject) {
    ...
}

You want to sort these GameObjects by their distance to the player, in order to get the nth closer object. The easiest way to do that is to sort the objects. For example:

List<GameObject> sortedGameObjects = 
    gameObjects.stream()
               .sorted(Comparator.comparingInt(gameObject -> computeDistanceToPlayer(gameObject)))
               .collect(Collectors.toList());

You can then get the nth element from that list.

You can even get the nth element from the stream directly:

GameObject nthCloserGameObject = 
    gameObjects.stream()
               .sorted(Comparator.comparingInt(gameObject -> computeDistanceToPlayer(gameObject)))
               .skip(n - 1)
               .findFirst()
               .orElse(null);

That is all you need, but, if the distance computation is costly (requires a long, costly computation), then it's not really optimal because it computes the distance of the same GameObject several times: each time it's compared to another GameObject during the sorting. So, if you want to avoid that, you can just compute all the distances first and associate them with the GameObjects, and then sort the resulting results:

public class GameObjectWithDistance {
    private final GameObject gameObject;
    private final int distance;

    // constructor, getters omitted for brevity
}

Now you just need to wrap every GameObject inside a GameObjectWithDistance, and sort the result:

GameObject nthCloserGameObject = 
    gameObjects.stream()
               .map(gameObject -> new GameObjectWithDistance(gameObject, computeDistanceToPlayer(gameObject)))
               .sorted(Comparator.comparingInt(GameObjectWithDistance::getDistance))
               .skip(n - 1)
               .findFirst()
               .map(GameObjectWithDistance::getGameObject)
               .orElse(null);

Now, if you're unfamiliar with streams and lambdas, you can do that with loops, lists and a comparator class, it doesn't matter. What matters is the logic, and the realization that you don't need any 2D list of anything like that.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Encountering a few problems implementing but i'll keep working on it. Also did you actually mean ".findFirst()" instead of ".first()"? first is unknown. – Fluidic Ice Aug 18 '17 at 11:30
  • Yes, sorry for that. – JB Nizet Aug 18 '17 at 12:58
  • Very close now, my code just isn't updating every step when I move around, and sometimes doesn't select the obvious nth nearest object. Do you see any problems with it? Thanks again for you help so far: https://pastebin.com/hiB39QzF – Fluidic Ice Aug 18 '17 at 13:23
  • I have a hard time understanding why the method takes x and y as argument but doesn't use them, and why you cast the distance to an int. If it's an int, then you don't need to cast. If it's something else like a double, then use comparingDouble() instead of comparingInt(). – JB Nizet Aug 18 '17 at 13:29
  • I took out those x,y to make it simpler and thanks for picking up that comparingInt() which I have changed to Double. Plus there were a bunch of other mistakes I made. I've got it working!! Thanks so much for your help. – Fluidic Ice Aug 18 '17 at 14:23
-1

If I understand your problem correctly, you need to store the distances temporarily with the GameObject and sort by the distances. After that, you will need to get the nth GameObject.

You can store the "GameObjects" and "distances" in a Map<GameObject, Integer>, and you can sort them easily using Java 8. After that, you can get the keySet from the map as a list and get the nth.

For example, you have a Map: (For simplicity I am assuming that the GameObject constructor takes an integer, just for example )

 Map<GameObject, Integer> map = new HashMap<GameObject, Integer>();
    map.put(new GameObject(1), 2);
    map.put(new GameObject(3), 1);
    map.put(new GameObject(2), 5);

To get the Map, sorted by distances:

HashMap<GameObject, Integer> sortedMap = map.entrySet().stream()
    .sorted(Map.Entry.<GameObject, Integer>comparingByValue())
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                    (oldValue, newValue) -> oldValue, LinkedHashMap::new));

Now if we print the values of this map , we get:

 System.out.println(collect.values());

output:

[1, 2, 5]

So now the KeySet of your sortedMap is the sorted list of GameObjects, from which you can easily get the nth:

List<GameObject> keySet = (List<GameObject>) collect.keySet();
keySet.get(n-1);

Hope that helps!

OutOfMind
  • 874
  • 16
  • 32
  • 1
    No, that doesn't make sense. keySet() doesn't return a list, but a Set. And there is really no point in storing all the entries of the map into a new HashMap just to get back its keys. You can map the sorted stream of entries to a stream of keys, and take the nth key directly (or collect to a list if you really want the sorted list). – JB Nizet Aug 18 '17 at 08:54