0

I have a SceneKit project into which I'm trying to integrate GameplayKit's 2D pathfinding capabilities.

What I have are 2 different kinds of obstacles (let's call them "good obstacles" and "bad obstacles"). The main character can touch the good ones, but not the bad ones (or it's level failed).

What I need GameplayKit to do if there's a "bad obstacle" in the way of the path is to stop pathfinding and notify me that the process failed.

How might I accomplish this?

Here's some code to illustrate that I do have pathfinding working -- minus the desired behavior, of course:

var obstacles = [GKPolygonObstacle]()

//Manually plot a set of points around the circumference of a circle...
//...for use in creating the obstacle (because I was unable to get `GKCircleObstacle` working)
let pegRadius: Float = 0.15
let numOfPoints = 12
let totalRadians: Float = 360.0 * .pi / 180.0
let radiansPerPoint: Float = totalRadians / Float(numOfPoints)
var polyPoints = [SIMD2<Float>]()
for i in 0..<numOfPoints {
   let angle = radiansPerPoint * Float(i)
   let x = pegPosition.x + (pegRadius * cos(angle))
   let y = pegPosition.y + (pegRadius * sin(angle))
         
   polyPoints.append(SIMD2(x: x, y: y))
}

//Create the obstacle:
let obstacle = GKPolygonObstacle(points: polyPoints)

obstacles.append(obstacle)


let graph = GKObstacleGraph(obstacles: obstacles, bufferRadius: 0.05)

//Grab the nodes that will act as "to" and "from":
let receptors = scnView.scene?.rootNode.childNodes(passingTest: { theNode, stop in
     return theNode.name == "receptors"
}) as? [SCNNodeIdentifiable]
var receptorIndex: Int = 0
guard let receptors = receptors else {
     return
}
    
                
var startPointVector = vector_float2(x: Float(receptors[receptorIndex].presentation.worldPosition.x), y: Float(receptors[receptorIndex].presentation.worldPosition.y))
let endPointVector = vector_float2(x: Float(receptors[receptorIndex+1].presentation.worldPosition.x), y: Float(receptors[receptorIndex+1].presentation.worldPosition.y))
var startingPoint = GKGraphNode2D(point: startPointVector)
let endPoint = GKGraphNode2D(point: endPointVector)

graph.connectUsingObstacles(node: startingPoint)
graph.connectUsingObstacles(node: endPoint)
var path = (graph as GKGraph).findPath(from: startingPoint, to: endPoint)
                
if path.count >= 2 {
   //Path found! Do something.
} else {
   //I'd like to end up here if there's a "bad obstacle" in the way of the pathfinding
}

I suspect that the answer has to do with GKRule and GKRuleSystem, but I could use some input.

Thanks for your help!

West1
  • 1,430
  • 16
  • 27

1 Answers1

0

So, according to the documentation, the correct way to handle this is to subclass GKGraphNode2D and override the cost(to:) method, which the findPath methods use in constructing a path through the graph.

On cost(to:), the docs say the following:

"Subclasses can implement this method to add other information to the cost. For example, a game might use higher costs to represent travel through regions of a map that are difficult for a character to traverse."

Unfortunately, it appears that this is totally broken and does not work.

Consider the following attempt at making "bad" obstacles have a higher cost than "good" ones:

class MyGraphNode2Dsubclass: GKGraphNode2D {
    var isBadObstacle: Bool = false
    
    override func cost(to node: GKGraphNode) -> Float {
       isBadObstacle ? 10.0 : super.cost(to: node)
    }
    
}

When it gets to super.cost(to: node), I get an EXC_BAD_ACCESS crash 100% of the time.

Now, what I can do is this:

class MyGraphNode2Dsubclass: GKGraphNode2D {
    var isBadObstacle: Bool = false
    
    override func cost(to node: GKGraphNode) -> Float {
       isBadObstacle ? 10.0 : 0.1
    }
    
}

But that results in all "good" obstacles having the same cost of 0.1 instead of having their true costs, so it's not a good solution.

Obviously, if the cost system of GameplayKit doesn't really work, then GameplayKit itself doesn't really work.

There are some other questions from years past that might involve this same problem -- but I'm not sure. They might be describing something a bit different, but on the same topic.

Of course, it's also possible that I've misunderstood this problem -- so please let me know if that's the case.

West1
  • 1,430
  • 16
  • 27