0 Replies
      Latest reply on May 19, 2020 2:44 PM by jean-victor
      jean-victor Level 1 Level 1 (0 points)

        Hi everyone !

         

        I'm having a pathfinder class in a SpriteKit game on Mac that a I want to use to process every path request in the game. So I have my class stored in my SKScene and I access it from different parts of the game always from the main thread. The pathfinder uses a GKGridGraph of a pretty good size (288 x 224 nodes). The class holds an array of requests processed one after another at each update() call from the main scene. Here is the code :

         

         

         

        class PathFinder {
                   
                var isLookingForPath = false
               
                var groundGraph : GKGridGraph?
                var queued : [PathFinderRequest] = []
               
                var thread: DispatchQoS.QoSClass = .userInitiated
               
                func generate(minPoint: CGPoint) {
                    // generate the groundGraph grid
                }
               
                func update() {
                    // called every frame
                    if !self.isLookingForPath {
                        findPath()
                    }
                   
                }
           
               
                func findPath(from start: TuplePosition, to end: TuplePosition, on layer: PathFinderLayer, callBack: PathFinderCallback) {
                    // Generating request
                   
                    let id = String.randomString(length: 5)
                    let request = PathFinderRequest(id: id, start: start, end: end, layer: layer, callback: callBack)
                   
                    // Append the request object at the end of the array
                    queued.append(request)
                }
               
               
                func findPath() {
                    self.isLookingForPath = true
                   
                    guard let request = queued.first else {
                        isLookingForPath = false
                        return
                    }
                   
                    let layer = request.layer
                    let callback = request.callback
                    let start = request.start
                    let end = request.end
                    let id = request.id
                   
                    var graph = self.groundGraph
                   
                    queued.removeFirst()
                   
                   
                    let findItem = DispatchWorkItem {
                        if let g = graph, let sn = g.node(atGridPosition: start.toVec()), let en = g.node(atGridPosition: end.toVec()) {
                            if let path = g.findPath(from: sn, to: en) as? [GKGridGraphNode], path.count > 0 {
                                // Here we have the path found
                                // it worked !
                               
                            }
                        }
                       
                        // Once the findPath() method execution is over,
                        // we reset the "flag" so we can call it once again from
                        // the update() method
                        self.isLookingForPath = false
                       
                    }
                   
                    // Execute the findPath() method in the chosen thread
                    // asynchronously
                    DispatchQueue.global(qos: thread).async(execute: findItem)
                   
                }
               
                func drawPath(_ path: [GKGridGraphNode]) {
                    // draw the path on the scene
                }
               
            }

         

         

         

        Well the code works quite good as it is. If I send random path request within (x+-10, y+-10) length it will return them to each object holding the callback in the request object pretty quickly, but suddenly one request is randomly taking a huge amount of time (approximatively 20s compared to 0.001s) and despite everything I tried I wasn't able to find out what happens. It's never on the same path, never the same caller, never after a certain amount of time... here is a video of the issue : https://www.youtube.com/watch?v=-IYlLOQgJrQ

         

         

        It sure happens more quickly when there is too much entities requesting but I can't figure why I'm sure it has to deal with the DispacthQueue async calls that I use to prevent the game from freezing.

         

         

        With delay on every call, the error appear later but is still here :

        DispatchQueue.global(qos: thread).asyncAfter(deadline: .now() + 0.1, execute: findItem)

         

         

        When I look for what is taking so much time to process it is a sub method of the GKGridGraph class :

        https://i.stack.imgur.com/0nZ2E.jpg

         

         

        So I really don't know how to figure this out, I tried everything I could think of but it always happens whatever the delay, the number of entities, the different threads, etc...

         

        Also I couldn't reproduce the issue on a playground with approximately the same code, however in that case the execution is way longer for every calculus.

         

         

        Thank you for your precious help !

         

        PS :  this post is on stackoverflow since a few days : https://stackoverflow.com/questions/61562600/apparently-random-execution-time-with-gkgraph-findpath-method-in-swift