Search code examples
swiftpathsprite-kitscale

How to make a sprite that follow a circular path while its size changes?


I already made that the ball follows a circular path, but the only problem is that I want that when touches began the path grow and the ball keep following the path. I have tried adding an SKAction but the ball doesn't follows the path. Hope someone can help me.

class GameScene: SKScene {

var circuloPrincipal = SKSpriteNode(imageNamed: "circulo")
var circuloFondo = SKSpriteNode(imageNamed: "circuloFondo")

override func didMoveToView(view: SKView) {

  circuloPrincipal.size = CGSize(width: 35, height: 35)
  circuloPrincipal.position = CGPoint(x: frame.width / 2, y: frame.height / 2 - 105)

  circuloPrincipal.color =  UIColor(red: 0.2, green: 0.9, blue: 0.6, alpha: 1.0)
  circuloPrincipal.colorBlendFactor = 1.0
  circuloPrincipal.zPosition = 3.0

  circuloFondo.size = CGSize(width: 300, height: 300)
  circuloFondo.position = CGPoint(x: frame.width / 2, y: frame.height / 2)
  self.addChild(circuloFondo)
  circuloFondo.color = UIColor(red: 0.2, green: 0.9, blue: 0.6, alpha: 1.0)
  circuloFondo.colorBlendFactor = 1.0
  circuloFondo.zPosition = 1.0

  circuloPrincipal.position = CGPointMake( CGRectGetMidX(frame) , (CGRectGetMidY(frame) + circuloFondo.size.width/2) )
  addChild(circuloPrincipal)
  let dx = circuloPrincipal.position.x - frame.width / 2
  let dy = circuloPrincipal.position.y - frame.height / 2

  let radius = (circuloFondo.frame.size.width / 2) - 20.0

  let radian = atan2(dy, dx)
  let playerPath = UIBezierPath(
     arcCenter: CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMidY(frame)),
     radius: radius,
     startAngle: radian,
     endAngle: radian + CGFloat(M_PI * 4.0),
     clockwise: true)

  let follow = SKAction.followPath(playerPath.CGPath, asOffset: false, orientToPath: true, speed: 200)
  circuloPrincipal.runAction(SKAction.repeatActionForever(follow))

}

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
  circuloFondo.runAction(SKAction.scaleTo(0.5, duration: 5))
}

//New code

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
   /* Called when a touch begins */

    //fondoTimer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("fondoEfecto"), userInfo: nil, repeats: true)

    if (circuloFondo.actionForKey("scaleTo") == nil) {
        let scale = SKAction.scaleTo(0.5, duration: 5)
        circuloFondo.runAction(scale,withKey:"scaleTo",optionalCompletion: {
 //here the error occours         


   // scaleTo is terminated
            // stop followTrackPath
            self.circuloPrincipal.removeActionForKey("followTrackForever")
            // re-run in the new path
            let follow = SKAction.followPath(playerPath.CGPath, asOffset: false, orientToPath: true, speed: 200)
            self.circuloPrincipal.runAction(SKAction.repeatActionForever(follow),withKey: "followTrackForever")
        })
    }

enter image description here


Solution

  • I think you could make some corrections that can help you for the rest of the project.

    This below works well with Swift 2.x, don't try it with Swift 3.x:

    First of all I want to introduce this extension that can be useful :

    extension SKNode
    {
        func runAction( action: SKAction!, withKey: String!, optionalCompletion: dispatch_block_t? )
        {
            if let completion = optionalCompletion
            {
                let completionAction = SKAction.runBlock( completion )
                let compositeAction = SKAction.sequence([ action, completionAction ])
                runAction( compositeAction, withKey: withKey )
            }
            else
            {
                runAction( action, withKey: withKey )
            }
        }
    
        func actionForKeyIsRunning(key: String) -> Bool {
            if self.actionForKey(key) != nil {
                return true
            } else {
                return false
            }
        }
    }
    

    Usage:

    self.runAction(myAction,withKey: "myActionName",optionalCompletion: {
        // do whatever you want when action is finished..
    })
    
    if self.actionForKeyIsRunning("myActionName") {
       // this action is up
    }
    

    Where to place this extension:

    You can place this extension also after the last brace of your class, like this example:

    class MyClass: SKScene {
       ...
    }  // end of MyClass
    
    extension SKNode
    {
       ...
    }
    

    This line could become:

        circuloPrincipal.runAction(SKAction.repeatActionForever(follow),withKey: "followTrackForever"))
    

    and the scaleTo lines could be:

        if !circuloFondo.actionForKeyIsRunning("scaleTo") {
                let scale = SKAction.scaleTo(0.5, duration: 5)
                circuloFondo.runAction(scale,withKey:"scaleTo",optionalCompletion: {
                   // scaleTo is terminated
                   // stop followTrackPath
                   self.circuloPrincipal.removeActionForKey("followTrackForever")
                   // re-run in the new path 
                   let follow = SKAction.followPath(playerPath.CGPath, asOffset: false, orientToPath: true, speed: 200)
                   self.circuloPrincipal.runAction(SKAction.repeatActionForever(follow),withKey: "followTrackForever"))
                })
        }