0

So I was trying to implement pathfinding in my SpriteKit game (the user touches the screen and a sprite makes its way to the touched point while avoiding obstacles). The following code is what i've gone for (this snippet is located in the custom class of the SpriteNode i want to move):

func move(location : CGPoint) {
    let obstacles = SKNode.obstacles(fromNodeBounds: self.scene!.children.filter({ (n) -> Bool in
        if (n.name ?? "" == "obstacle") {return true} else {return false}
    }))
    let graph = GKObstacleGraph.init(obstacles: obstacles, bufferRadius: 24)
    let startNode = GKGraphNode2D.init(point: float2(Float(self.position.x), Float(self.position.y)))
    let endNode = GKGraphNode2D.init(point: float2(Float(location.x), Float(location.y)))
    graph.connectUsingObstacles(node: startNode)
    graph.connectUsingObstacles(node: endNode)
    let path : [GKGraphNode] = graph.findPath(from: startNode, to: endNode)
    if (path.count <= 0) {return}
    var actions = [SKAction]()
    var bef = self.position
    for n in path {
        if let point2d = n as? GKGraphNode2D {
            let point = CGPoint(x: CGFloat(point2d.position.x), y: CGFloat(point2d.position.y))
            let moveAction = SKAction.move(to: point, duration: Double(distance(bef, point) * speed_multiplier))
            let rotateAction = SKAction.rotate(toAngle: angle(bef, point), duration: 0, shortestUnitArc: true)
            bef = point
            actions.append(rotateAction)
            actions.append(moveAction)
        }
    }
    let sequence = SKAction.sequence(actions)
    self.run(sequence)
}

i call this function from touchesMoved in my gamescene-Class:

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    ...
    let location = ((touches.first as? UITouch)?.location(in: self))!
    ship.move(location: location)
    ...
}

This seems to work fine when i only tap the screen once, but when i tap and hold it, it moves a lot slower and the sprite starts to flicker. I tried fixing this for an hour now and i just have no idea what causes this weird behavior.

Thank you for your help!

Ferb300
  • 53
  • 4
  • You may be queuing up a bunch of conflicting animations, be sure to cancel the previous actions before starting a new set. Touches moved can be called alot of times and this code will keep creating paths and sequences and starting them. (i'm no game development expert btw, just reviewing the code) – Scriptable Jun 12 '19 at 14:20
  • also it seems like your trying to get it to follow your finger... but the code it trying to work out and run the complete path from it to the touched location, each time your finger moves it tries to recalculate and animate the whole route. Maybe you just need to get the ship heading the in right direction and 'look-ahead' for obstacles to avoid, you don't need to add the whole sequence from one point to another if the destination is going to change constantly – Scriptable Jun 12 '19 at 14:23
  • I actually tried cancelling animations before starting the new one by adding `ship.removeAllActions()` in `touchesMoved()` but that only resulted in the sprite not moving at all, and im not quite sure why – Ferb300 Jun 12 '19 at 14:28
  • It's not free, but I would highly recommend this ebook for what you are doing: https://store.raywenderlich.com/products/2d-apple-games-by-tutorials it teaches you alot of these type of concepts – Scriptable Jun 12 '19 at 14:35
  • Your ship does not move because you cancel the action before an update can occur. You do not want to be cancelling actions, you want to add additional actions that fire when your action is completed. This can get somewhat complex. Also, you are doing a lot of repetitive steps that suck up a lot of CPU time. You do not need to create your graph on touchmoved. Create it on touch begin, and then append your actions on moved – Knight0fDragon Jun 12 '19 at 14:57
  • Thanks everyone for taking time to help me. Based on your ideas, i found a solution, but I'm not sure whether it is "good style". I#ve splitted the pathfinding function into to parts (the creating of the graph and the finding of the path) one of them being called in `touchesBegan()` and the other one in `touchesMoved'. (1/2) – Ferb300 Jun 12 '19 at 17:15
  • In the pathfinding part, i added a part which checks if a certain bool varriable is true, and only when it is, it sets that variable back to false and starts the pathfinding and the animation. secondly, i added a timer that resets this variable to true only every 400 milliseconds, to stop the function from being executed to much. It works perfectly fine now, but id love to hear feedback on whether this is a good solution or just a dirty workaround. (2/2) – Ferb300 Jun 12 '19 at 17:15
  • update your question here, and I would abandon the timer – Knight0fDragon Jun 12 '19 at 19:37
  • You know what would solve a lot of your problems? Do not let the sprite move till after you lift your finger off of the screen. – Knight0fDragon Jun 12 '19 at 19:39
  • this is actually how i implemented it the first time, but it really didnt feel good controlling the sprite like that – Ferb300 Jun 13 '19 at 07:16

0 Answers0