John Cleveland
John Cleveland

Reputation: 498

How to build an Apple GameplayKit GKGridGraph

I am building a turn-based strategy game using Swift and Apple's SpriteKit and GameplayKit frameworks. I am attempting to use the GameplayKit-provided GKGridGraph data structure to represent the map grid and am having problems figuring out how to construct the GKGridGraph. If you build an array of GKGridGraphNodes, then add them to an empty GKGridGraph using the add() method, the grid graph is not aware of the grid, even though the nodes are "in" the graph. See sandbox code below:

override func sceneDidLoad() {
    // Dimensions of our grid
    let width: Int32 = 4
    let height: Int32 = 4

    // Create an empty graph
    let graph = GKGridGraph()

    // Create a mutable array to hold the nodes
    var nodes: [GKGridGraphNode] = []

    // Build up the array of empty graph nodes
    for row in 0..<height {
        for col in 0..<width {
            let position = SIMD2<Int32>(Int32(col), Int32(row))
            nodes.append(GKGridGraphNode(gridPosition: position))
        }
    }

    // Add all the nodes
    graph.add(nodes)

    // Try to find each node in the grid.
    // No nodes are found using node() method.
    for row in 0..<height {
        for col in 0..<width {
            let pos = SIMD2<Int32>(Int32(col), Int32(row))

            if let _ = graph.node(atGridPosition: pos) {
                print("Found node at [\(row),\(col)]")
            }
        }
    }
}

The GKGridGraph method node(atGridPosition) will not find any of the nodes in the grid. The only way I have found to construct a GKGridGraph in such a way that makes it aware of its own grid is to use the constructor:

init(fromGridStartingAt: vector_int2, 
     width: Int32, 
     height: Int32, 
     diagonalsAllowed: Bool, 
     nodeClass: AnyClass)

which does construct a grid-aware graph. However, the only way I have figured out (based on reading books, websites, and API documentation) how to remove the nodes that I don't want to be traversable is to use the GKGraph remove() method. However, I have found the remove method's performance to be atrocious, so it is not going to work for my purposes to build a "complete" grid graph, then go back and remove the nodes I don't want to be in the graph. I do not know why the performance of remove() is so bad (possibly having to sever all of the adjacent connections), but for a large grid it seems almost unusable.

I have also tried manually adding all the node-to-node connections after I added the array of nodes, and that has no effect on being able to use the node() method to retrieve a node at a specific grid position.

Does anyone have any idea how to do what I am trying to accomplish: fully construct a GKGridGraph using only the nodes initially that I want to be in the graph?

Upvotes: 1

Views: 207

Answers (1)

Wattera
Wattera

Reputation: 21

The remove method of GKGridGraph has terrible performance as you say, after few hours I found a fix that works

The main idea is to never use the remove method and instead only disconnect and reconnect manually the nodes.

Here is an example of a wallNode that you want to remove from the gridGraph

let wallNode = gridGraph.node(gridPosition: vector(Int32(column), Int32(row))
wallNode.removeConnections(to: wallNode.connectedNodes, bidirectionnal: true)

For your specific problem, you should just do this with all the nodes in the graph and then reconnect those you want

Upvotes: 0

Related Questions