7

Is there a way of getting the closest nodes to a node?

I'm just about to write a method to iterate all nodes and calculate distances etc... but wondered if there is a better way?

I have 30 nodes and need the 2 nearest nodes to each of the 30 nodes (if that makes sense).

Fogmeister
  • 76,236
  • 42
  • 207
  • 306
  • 5
    I don't know if SpriteKit has a ready-made method to do this. As a general algorithm question, the overhead involved in optimizing the search exceeds the benefits unless you have 100's of nodes. As a side note, I wonder if you have considered that the results of searching for the two nearest nodes lacks symmetry. In other words, if B and C are the two nearest nodes to A, that doesn't necessarily mean that A is one of the two nearest nodes to B. – user3386109 Mar 28 '14 at 23:54
  • Yeah, I had already thought of the lack of symmetry. In the end I did it by sorting the array of nodes (by distance to a given node) and returned the subarray of the size I needed. Probably not that efficient but I'm experimenting... and it's 1am. :D – Fogmeister Mar 29 '14 at 01:09
  • 3
    one optimization strategy is called spatial partitioning, but as said above it's probably overkill without hundreds of nodes – CodeSmile Mar 29 '14 at 07:45
  • 1
    Unless you have hundreds of nodes *OR* if you're doing it 100's of times a second. – Oxcug Jul 03 '14 at 21:23

2 Answers2

6

As of iOS 10, you can use the Spatial Partitioning features of GampelayKit. In 2D either GKQuadtree or GKRTree depending on your needs.

From the docs:

Quadtrees and R-trees have different performance tradeoffs for different tasks: quadtrees can be faster when objects are more uniformly distributed in space or when their positions change frequently, and R-trees can be faster when searching for all objects in a given region.

Add your enemies to the tree:

    let minX = Float(enemy.frame.minX)
    let minY = Float(enemy.frame.minY)
    let maxX = Float(enemy.frame.maxX)
    let maxY = Float(enemy.frame.maxY)

    var enemiesRTree = GKRTree(maxNumberOfChildren: 3)
    enemiesRTree.addElement(enemy,
                             boundingRectMin: vector2(minX, minY),
                             boundingRectMax: vector2(maxX, maxY),
                             splitStrategy: GKRTreeSplitStrategy.linear)

Then you can search by area.

    let enemiesInProximity = enemiesRTree.elements(
       inBoundingRectMin: vector2(0, 0),
       rectMax: vector2(100, 100))

You can then create the search area e.g. relative to the player's position.

Adam Eri
  • 889
  • 7
  • 13
1

I believe that your approach is the most appropriate for this situation. If you stored all the nodes in an array style there may be a relatively efficient way to do what you mentioned above.

In any situation, you still may be tasked with having three nodes closest. If this is not a problem, you use a method that takes a nodes position and then use for loop to project "invisible circles" of increasing radius until a circle finally contains exactly 2 nodes. Then just return those nodes.

meisenman
  • 1,818
  • 1
  • 15
  • 25