用SKScene和SKActions替换CALayer和CABasicAnimation [英] replacing CALayer and CABasicAnimation with SKScene and SKActions

查看:50
本文介绍了用SKScene和SKActions替换CALayer和CABasicAnimation的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试对Metaballs上的



更新(此部分开始模拟静态球和动态球的运动左右,但没有元球动画)

 类GameScene:SKScene {
var dBCircle:SKShapeNode!
let radiusDBCircle:CGFloat = 10
let radiusBall:CGFloat = 15
private let SCALE_RATE:CGFloat = 0.3
覆盖func didMoveToView(view:SKView){
//一些参数
let strokeColor = SKColor.orangeColor()
dBHeight = CGRectGetMaxY(self.frame)-84 // 64 navigationController高度+ 20合理距离
let dBStartX = CGRectGetMidX(self.frame )-160 //最左端
让dBStopX = CGRectGetMidX(self.frame)+160 //最右端
让dBWidth = dBStopX-dBStartX
let totalBalls = 7 //第一个和最后一个将被隐藏
let ballArea = dBWidth / CGFloat(totalBalls-1)
let distanceBtwBalls =((ballArea-(radiusBall * 2))+ radiusBall * 2)

//创建dbCircle
dBCircle = SKShapeNode.init(circleOfRadius:radiusDBCircle)
dBCircle.position = CGPointMake(CGRectGetMidX(self.frame),dBHeight)
dBCircle.strokeCo lor = strokeColor
dBCircle.name = dBCircle
dBCircle.fillColor = UIColor.clearColor()
addChild(dBCircle)
//为我制作静态球
in 0 ..< totalBalls {
let ball = SKShapeNode.init(circleOfRadius:radiusBall)
ball.position = CGPointMake(dBStartX +(distanceBtwBalls * CGFloat(i)),dBHeight)
球.strokeColor = strokeColor
ball.name = ball
ball.fillColor = UIColor.clearColor()
如果i == 0 || i == totalBalls-1 {
ball.hidden = true
}
addChild(ball)
}
mediaTimingFunctionEaseInEaseOutEmulate(dBCircle,dBStartX:dBStartX,dBStopX:dBStopX)
}
func mediaTimingFunctionEaseInEaseOutEmulate(node:SKShapeNode,dBStartX:CGFloat,dBStopX:CGFloat){
let actionMoveLeft = SKAction.moveToX(dBStartX,持续时间:1.7)
actionMoveLeft.timingMode = SKActionTimingMode.EaseInEaseOut

let actionMoveRight = SKAction.moveToX(dBStopX,持续时间:1.7)
actionMoveRight.timingMode = SKActionTimingMode.EaseInEaseOut

node.runAction(SKAction.repeatActionForever (SKAction.sequence([actionMoveLeft,actionMoveRight])))
}
覆盖func update(currentTime:NSTimeInterval){
var i = 0
self.enumerateChildNodesWithName( ball ){
节点,停在
中,让ball =节点为! SKShapeNode

如果CGRectContainsRect(ball.frame,self.dBCircle.frame){
if(ball.actionForKey( zoom)== nil){
让zoomIn = SKAction .scaleTo(1.5,持续时间:0.25)
let zoomOut = SKAction.scaleTo(1.0,持续时间:0.25)
let seq = SKAction.sequence([zoomIn,zoomOut])
ball.runAction (seq,withKey: zoom)
}
}
i + = 1
}
}
}



带有元球动画的新更新



最后我已经意识到了这一结果,我的目标是使其与原始版本非常相似:





是否可以对时间进行一些更改(例如zoomIn或zoo mOut时间值或actionMoveLeft,actionMoveRight时间值),这是代码:

  import SpriteKit 
类GameScene:SKScene {
var dBCircle:SKShapeNode!
let radiusDBCircle:CGFloat = 10
let radiusBall:CGFloat = 15
private let SCALE_RATE:CGFloat = 0.3
覆盖func didMoveToView(view:SKView){
//一些参数
let strokeColor = SKColor.orangeColor()
dBHeight = CGRectGetMaxY(self.frame)-84 // 64 navigationController高度+ 20合理距离
let dBStartX = CGRectGetMidX(self.frame )-160 //最左端
let dBStopX = CGRectGetMidX(self.frame)+160 //最右端
let dBWidth = dBStopX-dBStartX
let totalBalls = 7 //第一个和最后一个will被隐藏
let ballArea = dBWidth / CGFloat(totalBalls-1)
let distanceBtwBalls =((ballArea-(radiusBall * 2))+ radiusBall * 2)

//创建dbCircle
dBCircle = SKShapeNode.init(circleOfRadius:radiusDBCircle)
dBCircle.position = CGPointMake(CGRectGetMidX(self.frame,dBHeight)
dBCircle.strokeCo lor = strokeColor
dBCircle.name = dBCircle
dBCircle.fillColor = UIColor.clearColor()
addChild(dBCircle)
//为我制作静态球
in 0 ..< totalBalls {
let ball = SKShapeNode.init(circleOfRadius:radiusBall)
ball.position = CGPointMake(dBStartX +(distanceBtwBalls * CGFloat(i)),dBHeight)
球.strokeColor = strokeColor
ball.name = ball
ball.fillColor = UIColor.clearColor()
如果i == 0 || i == totalBalls-1 {
ball.hidden = true
}
addChild(ball)
}
mediaTimingFunctionEaseInEaseOutEmulate(dBCircle,dBStartX:dBStartX,dBStopX:dBStopX)
}
func mediaTimingFunctionEaseInEaseOutEmulate(node:SKShapeNode,dBStartX:CGFloat,dBStopX:CGFloat){
let actionMoveLeft = SKAction.moveToX(dBStartX,duration:2.5)
actionMoveLeft.timingMode = SKActionTimingMode.EaseInEaseOut

let actionMoveRight = SKAction.moveToX(dBStopX,持续时间:2.5)
actionMoveRight.timingMode = SKActionTimingMode.EaseInEaseOut

node.runAction(SKAction.repeatActionForever (SKAction.sequence([actionMoveLeft,actionMoveRight])))
}
// MARK:-_metaball原始函数
func _metaball(circle2:SKShapeNode,circle1:SKShapeNode,v:CGFloat,handeLenRate :CGFloat,maxDistance:CGFloat,vanishingTime:NSTimeInterval = 0.015){
let center1 = circle1.position
let center2 = circle2.position
let d = center1.distance(center2)
var radius1 = radiusDBCircle
var radius2 = radiusBall
如果(radius1 == 0 || radius2 == 0){
return
}
var u1:CGFloat = 0.0
var u2:CGFloat = 0.0
if(d> maxDistance || d< ; = abs(radius1-radius2)){
return
}否则if(d u1 = acos((radius1 * radius1 + d * d-radius2 * radius2)/(2 * radius1 * d))
u2 = acos((radius2 * radius2 + d * d-radius1 * radius1)/(2 * radius2 * d))
} else {
u1 = 0.0
u2 = 0.0
}
let angle1 = center1.angleBetween(center2)
let angle2 = acos((radius1-radius2)/ d)
let angle1a =角度1 + u1 +(angle2-u1)* v
let angle1b =角度1-u1-(angle2-u1)* v
let angle2a =角度1 + CGFloat(M_PI)-u2-( CGFloat(M_PI)-u2-angle2)* v
让angle2b = angle1-CGFloat(M_PI)+ u2 +(CGFloat(M_PI)-u2-angle2)* v
让p1a = center1.po int(弧度:angle1a,withLength:radius1)
let p1b = center1.point(弧度:angle1b,withLength:radius1)
let p2a = center2.point(弧度:angle2a,withLength:radius2)
let p2b = center2.point(弧度:angle2b,withLength:radius2)
let totalRadius = radius1 + radius2
var d2 = min(v * handeLenRate,p1a.minus(p2a).length() / totalRadius)
d2 * = min(1,d * 2 / totalRadius)
radius1 * = d2
radius2 * = d2
let cp1a = p1a.point(弧度:angle1a -CGFloat(M_PI_2),withLength:radius1)
let cp2a = p2a.point(radians:angle2a + CGFloat(M_PI_2),withLength:radius2)
let cp2b = p2b.point(radians:angle2b-CGFloat (M_PI_2),withLength:radius2)
let cp1b = p1b.point(弧度:angle1b + CGFloat(M_PI_2),withLength:radius1)
let pathJoinedCircles = UIBezierPath()
pathJoinedCircles.moveToPoint( p1a)
pathJoinedCircles.a ddCurveToPoint(p2a,controlPoint1:cp1a,controlPoint2:cp2a)
pathJoinedCircles.addLineToPoint(p2b)
pathJoinedCircles.addCurveToPoint(p1b,controlPoint1:cp2b,controlPoint2:cp1b)
pathJoined
pathJoinedCircles.closePath()
let shapeNode = SKShapeNode(路径:pathJoinedCircles.CGPath)
shapeNode.strokeColor = SKColor.orangeColor()
shapeNode.fillColor = UIColor.clearColor()
addChild(shapeNode)
let wait = SKAction.waitForDuration(vanishingTime)
self.runAction(wait,completion:{
shapeNode.removeFromParent()
})
}
覆盖功能更新(当前时间:NSTimeInterval){
var i = 0
self.enumerateChildNodesWithName( ball){
节点,在
中停止让ball =节点为! SKShapeNode
让expandFrame = CGRectMake(ball.frame.origin.x-self.radiusBall * 3,ball.frame.origin.y,ball.frame.width +(self.radiusBall * 6),ball.frame.height )
如果CGRectContainsRect(enlargeFrame,self.dBCircle.frame){
if(ball.actionForKey( zoom)== nil){
让zoomIn = SKAction.scaleTo(1.5,持续时间:0.25)
zoomIn.timingMode = SKActionTimingMode.EaseInEaseOut
let zoomOut = SKAction.scaleTo(1.0,持续时间:0.25)
let wait = SKAction.waitForDuration(0.8)
seq = SKAction.sequence([zoomIn,zoomOut,wait])
ball.runAction(seq,withKey: zoom)
}
}
self._metaball(ball,circle1 :self.dBCircle,v:0.6,handeLenRate:2.0,maxDistance:4 * self.radiusBall)
i + = 1
}
}
}
// MARK :-扩展
分机ension CGPoint {
函数距离(点:CGPoint)-> CGFloat {
let dx = point.x-self.x
let dy = point.y-self.y
return sqrt(dx * dx + dy * dy)
}
func angleBetween(point:CGPoint)-> CGFloat {
return atan2(point.y-self.y,point.x-self.x)
}
func point(弧度弧度:CGFloat,长度长度:CGFloat)-> CGPoint {
return CGPoint(x:self.x + length * cos(radians),y:self.y + length * sin(radians))
}
func minus(point:CGPoint )-> CGPoint {
return CGPoint(x:self.x-point.x,y:self.y-point.y)
}
func length()-> CGFloat {
return sqrt(self.x * self.x + self.y + self.y)
}
}



Swift 3:



(我对 max做了一些改动:4 * self.radiusBall maxDistance:5 * self.radiusBall 变得更接近原始版本,但您可以根据需要进行更改)

  import SpriteKit 
class GameScene:SKScene {
var dBCircle:SKShapeNode!
let radiusDBCircle:CGFloat = 10
let radiusBall:CGFloat = 15
private let SCALE_RATE:CGFloat = 0.3
覆盖func didMove(查看:SKView){
let标签= self.childNode(withName: // helloLabel)为? SKLabelNode
label?.removeFromParent()
self.anchorPoint = CGPoint.zero
//一些参数
让strokeColor = SKColor.orange
让dBHeight = self.frame .midY
让dBStartX = self.frame.midX-260 //极左
让dBStopX = self.frame.midX + 260 //极右
让dBWidth = dBStopX-dBStartX
let totalBalls = 7 //将隐藏第一个和最后一个
let ballArea = dBWidth / CGFloat(totalBalls-1)
let distanceBtwBalls =(((ballArea-(radiusBall * 2))+ radiusBall * 2 )

//创建dbCircle
dBCircle = SKShapeNode.init(circleOfRadius:radiusDBCircle)
dBCircle.position = CGPoint(x:self.frame.midX,y:dBHeight)
dBCircle.strokeColor =笔触颜色
dBCircle.name = dBCircle
dBCircle.fillColor = UIColor.clear
addChild(dBCircle)
//创建静态球
代表我0 ..< totalBalls {
let ball = SKShapeNode.init(circleOfRadius:radiusBall)
ball.position = CGPoint(x:dBStartX +(distanceBtwBalls * CGFloat(i)),y:dBHeight)
ball.strokeColor = strokeColor
ball.name = ball
ball.fillColor = UIColor.clear
如果i == 0 || i == totalBalls-1 {
ball.isHidden = true
}
addChild(ball)
}
mediaTimingFunctionEaseInEaseOutEmulate(node:dBCircle,dBStartX:dBStartX,dBStopX: dBStopX)
}
func mediaTimingFunctionEaseInEaseOutEmulate(node:SKShapeNode,dBStartX:CGFloat,dBStopX:CGFloat){
let actionMoveLeft = SKAction.moveTo(x:dBStartX,持续时间:2.5)
actionMoveLeft.timingMode = SKActionTimingMode.easeInEaseOut

let actionMoveRight = SKAction.moveTo(x: dBStopX, duration:2.5)
actionMoveRight.timingMode = SKActionTimingMode.easeInEaseOut

node .run(SKAction.repeatForever(SKAction.sequence([actionMoveLeft,actionMoveRight])))
}
//MARK: - _metaball original function
func _metaball(circle2:SKShapeNode, circle1:SKShapeNode , v: CGFloat, handeLenRate: CGFloat, maxDistance: CGFloat,vanishingTime : TimeInterval = 0.015) {
le t center1 = circle1.position
let center2 = circle2.position
let d = center1.distance(point: center2)
var radius1 = radiusDBCircle
var radius2 = radiusBall
if (radius1 == 0 || radius2 == 0) {
return
}
var u1: CGFloat = 0.0
var u2: CGFloat = 0.0
if (d > maxDistance || d <= abs(radius1 - radius2)) {
return
} else if (d < radius1 + radius2) {
u1 = acos((radius1 * radius1 + d * d - radius2 * radius2) / (2 * radius1 * d))
u2 = acos((radius2 * radius2 + d * d - radius1 * radius1) / (2 * radius2 * d))
} else {
u1 = 0.0
u2 = 0.0
}
let angle1 = center1.angleBetween(point: center2)
let angle2 = acos((radius1 - radius2) / d)
let angle1a = angle1 + u1 + (angle2 - u1) * v
let angle1b = angle1 - u1 - (angle2 - u1) * v
let angle2a = angle1 + CGFloat(M_PI) - u2 - (CGFloat(M_PI) - u2 - angle2) * v
let angle2b = angle1 - CGFloat(M_PI) + u2 + (CGFloat(M_PI) - u2 - angle2) * v
let p1a = cen ter1.point(radians: angle1a, withLength: radius1)
let p1b = center1.point(radians: angle1b, withLength: radius1)
let p2a = center2.point(radians: angle2a, withLength: radius2)
let p2b = center2.point(radians: angle2b, withLength: radius2)
let totalRadius = radius1 + radius2
var d2 = min(v * handeLenRate, p1a.minus(point: p2a).length() / totalRadius)
d2 *= min(1, d * 2 / totalRadius)
radius1 *= d2
radius2 *= d2
let cp1a = p1a.point(radians: angle1a - CGFloat(M_PI_2), withLength: radius1)
let cp2a = p2a.point(radians: angle2a + CGFloat(M_PI_2), withLength: radius2)
let cp2b = p2b.point(radians: angle2b - CGFloat(M_PI_2), withLength: radius2)
let cp1b = p1b.point(radians: angle1b + CGFloat(M_PI_2), withLength: radius1)
let pathJoinedCircles = UIBezierPath()
pathJoinedCircles.move(to: p1a)
pathJoin edCircles.addCurve(to: p2a, controlPoint1: cp1a, controlPoint2: cp2a)
pathJoinedCircles.addLine(to: p2b)
pathJoinedCircles.addCurve(to: p1b, controlPoint1: cp2b, controlPoint2: cp1b)
pathJoinedCircles.addLine(to: p1a)
pathJoinedCircles.close()
let shapeNode = SKShapeNode(path: pathJoinedCircles.cgPath)
shapeNode.strokeColor = SKColor.orange
shapeNode.fillColor = UIColor.clear
addChild(shapeNode)
let wait = SKAction.wait(forDuration: vanishingTime)
self.run(wait,completion: {
shapeNode.removeFromParent()
})
}
override func update(_ currentTime: TimeInterval) {
var i = 0
self.enumerateChildNodes(withName: \"ball\") {
node, stop in
let ball = node as! SKShapeNode
let enlargeFrame = CGRect(x:ball.frame.origin.x-self.radiusBall*3,y:ball.frame.origin.y,width:ball.frame.width+(self.radiusBall*6),height:ball.frame.height)
if enlargeFrame.contains(self.dBCircle.frame) {
if (ball.action(forKey: \"zoom\") == nil) {
let zoomIn = SKAction.scale(to: 1.5, duration: 0.25)
zoomIn.timingMode = SKActionTimingMode.easeInEaseOut
let zoomOut = SKAction.scale(to: 1.0, duration: 0.25)
let wait = SKAction.wait(forDuration: 0.7)
let seq = SKAction.sequence([zoomIn,zoomOut,wait])
ball.run(seq,withKey: \"zoom\")
}
}
self._metaball(circle2: ball, circle1: self.dBCircle, v: 0.6, handeLenRate: 2.0, maxDistance: 5 * self.radiusBall)
i += 1
}
}
}
//MARK: - Extens ions
extension CGPoint {
func distance(point: CGPoint) -> CGFloat {
let dx = point.x - self.x
let dy = point.y - self.y
return sqrt(dx * dx + dy * dy)
}
func angleBetween(point: CGPoint) -> CGFloat {
return atan2(point.y - self.y, point.x - self.x)
}
func point(radians: CGFloat, withLength length: CGFloat) -> CGPoint {
return CGPoint(x: self.x + length * cos(radians), y: self.y + length * sin(radians))
}
func minus(point: CGPoint) -> CGPoint {
return CGPoint(x: self.x - point.x, y: self.y - point.y)
}
func length() -> CGFloat {
return sqrt(self.x * self.x + self.y + self.y)
}
}


I am trying to restructure this Github Swift project on Metaballs so that the circles are represented by SKShapeNodes that are moved around by SKActions instead of CABasicAnimation.

I am not interested in the various Metaball parameters (the handleLenRate, Spacing and so on) appearing in the viewController. I basically want to be able to specify a start and end position for the animation using SKActions.

I am not certain how to achieve this, especially how to replace the startAnimation function below with SKShapeNode's with SKActions:

    func startAnimation() {
    let loadingLayer = self.layer as! DBMetaballLoadingLayer
    loadingAnimation = CABasicAnimation(keyPath: "movingBallCenterX")
    loadingAnimation!.duration = 2.5
    loadingAnimation!.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
    loadingAnimation!.fromValue = NSValue(CGPoint:fromPoint)
    loadingAnimation!.toValue = NSValue(CGPoint: toPoint)
    loadingAnimation!.repeatCount = Float.infinity
    loadingAnimation!.autoreverses = true
    loadingLayer.addAnimation(loadingAnimation!, forKey: "loading")
}

Please see what I've been able to do below:

MBCircle class:

struct MBCircle {
var center: CGPoint = CGPointZero
var radius: CGFloat = 0.0
var frame: CGRect {
    get {
        return CGRect(x: center.x - radius, y: center.y - radius, width: 2 * radius, height: 2 * radius)
    }
  }
}

struct DefaultConfig {
    static let radius: CGFloat = 15.0
    static let mv: CGFloat = 0.6
    static let maxDistance: CGFloat = 10 * DefaultConfig.radius
    static let handleLenRate: CGFloat = 2.0
    static let spacing: CGFloat = 160.0
}

GameScene class (representing both the DBMetaballLoadingLayer and DBMetaballLoadingView):

class GameScene: SKScene {

private let MOVE_BALL_SCALE_RATE: CGFloat = 0.75
private let ITEM_COUNT = 2
private let SCALE_RATE: CGFloat = 1.0//0.3
private var circlePaths = [MBCircle]()
var radius: CGFloat = DefaultConfig.radius
var maxLength: CGFloat {
    get {
        return (radius * 4 + spacing) * CGFloat(ITEM_COUNT) 
    }
}
var maxDistance: CGFloat = DefaultConfig.maxDistance
var mv: CGFloat = DefaultConfig.mv
var spacing: CGFloat = DefaultConfig.spacing {
    didSet {
        _adjustSpacing(spacing)
    }
}
var handleLenRate: CGFloat = DefaultConfig.handleLenRate
var movingBallCenterX : CGFloat = 0.0 {
    didSet {
        if (circlePaths.count > 0) {
            circlePaths[0].center = CGPoint(x: movingBallCenterX, y: circlePaths[0].center.y)
        }
    }
}

func _generalInit() {
    circlePaths = Array(0..<ITEM_COUNT).map { i in
        var circlePath = MBCircle()
        circlePath.center = CGPoint(x: (radius * 10 + spacing) * CGFloat(i), y: radius * (1.0 + SCALE_RATE))
        circlePath.radius = i == 0 ? radius * MOVE_BALL_SCALE_RATE : radius
        circlePath.sprite = SKShapeNode(circleOfRadius: circlePath.radius)
        circlePath.sprite?.position = circlePath.center
        circlePath.sprite?.fillColor = UIColor.blueColor()
        addChild(circlePath.sprite!)
        return circlePath

    }
}

func _adjustSpacing(spacing: CGFloat) {
    if (ITEM_COUNT > 1 && circlePaths.count > 1) {
        for i in 1..<ITEM_COUNT {
            var circlePath = circlePaths[i]
            circlePath.center = CGPoint(x: (radius*2 + spacing) * CGFloat(i), y: radius * (1.0 + SCALE_RATE))
        }
    }
}

func _renderPath(path: UIBezierPath) {
    var shapeNode = SKShapeNode()
    shapeNode.path = path.CGPath
    shapeNode.fillColor = UIColor.blueColor()
    addChild(shapeNode)
   }

func _metaball(j: Int, i: Int, v: CGFloat, handeLenRate: CGFloat, maxDistance: CGFloat) {
    let circle1 = circlePaths[i]
    let circle2 = circlePaths[j]

    let center1 = circle1.center
    let center2 = circle2.center

    let d = center1.distance(center2)

    var radius1 = circle1.radius
    var radius2 = circle2.radius

    if (d > maxDistance) {
        _renderPath(UIBezierPath(ovalInRect: circle2.frame))

    } else {
        let scale2 = 1 + SCALE_RATE * (1 - d / maxDistance)
        radius2 *= scale2
        _renderPath(UIBezierPath(ovalInRect: CGRect(x: circle2.center.x - radius2, y: circle2.center.y - radius2, width: 2 * radius2, height: 2 * radius2)))

    }

    if (radius1 == 0 || radius2 == 0) {
        return
    }

    var u1: CGFloat = 0.0
    var u2: CGFloat = 0.0
    if (d > maxDistance || d <= abs(radius1 - radius2)) {
        return
    } else if (d < radius1 + radius2) {
        u1 = acos((radius1 * radius1 + d * d - radius2 * radius2) / (2 * radius1 * d))
        u2 = acos((radius2 * radius2 + d * d - radius1 * radius1) / (2 * radius2 * d))
    } else {
        u1 = 0.0
        u2 = 0.0
    }

    let angle1 = center1.angleBetween(center2)
    let angle2 = acos((radius1 - radius2) / d)
    let angle1a = angle1 + u1 + (angle2 - u1) * v
    let angle1b = angle1 - u1 - (angle2 - u1) * v
    let angle2a = angle1 + CGFloat(M_PI) - u2 - (CGFloat(M_PI) - u2 - angle2) * v
    let angle2b = angle1 - CGFloat(M_PI) + u2 + (CGFloat(M_PI) - u2 - angle2) * v

    let p1a = center1.point(radians: angle1a, withLength: radius1)
    let p1b = center1.point(radians: angle1b, withLength: radius1)
    let p2a = center2.point(radians: angle2a, withLength: radius2)
    let p2b = center2.point(radians: angle2b, withLength: radius2)

    let totalRadius = radius1 + radius2
    var d2 = min(v * handeLenRate, p1a.minus(p2a).length() / totalRadius)
    d2 *= min(1, d * 2 / totalRadius)
    radius1 *= d2
    radius2 *= d2

    let cp1a = p1a.point(radians: angle1a - CGFloat(M_PI_2), withLength: radius1)
    let cp2a = p2a.point(radians: angle2a + CGFloat(M_PI_2), withLength: radius2)
    let cp2b = p2b.point(radians: angle2b - CGFloat(M_PI_2), withLength: radius2)
    let cp1b = p1b.point(radians: angle1b + CGFloat(M_PI_2), withLength: radius1)

    let pathJoinedCircles = UIBezierPath()
    pathJoinedCircles.moveToPoint(p1a)
    pathJoinedCircles.addCurveToPoint(p2a, controlPoint1: cp1a, controlPoint2: cp2a)
    pathJoinedCircles.addLineToPoint(p2b)
    pathJoinedCircles.addCurveToPoint(p1b, controlPoint1: cp2b, controlPoint2: cp1b)
    pathJoinedCircles.addLineToPoint(p1a)
    pathJoinedCircles.closePath()
    _renderPath(pathJoinedCircles)
}

func startAnimation() {

}
override func didMoveToView(view: SKView) {

    _generalInit()

}

    override func update(currentTime: CFTimeInterval) {
    /* Called before each frame is rendered */
   }
}

I didn't make any changes to the CGPointExtension class.

UPDATE

I am still trying to get the metaball effect, this is my progress so far based on suggestions from Alessandro Ornano:

import SpriteKit

extension CGPoint {
func distance(point: CGPoint) -> CGFloat {
    let dx = point.x - self.x
    let dy = point.y - self.y
    return sqrt(dx * dx + dy * dy)
}

func angleBetween(point: CGPoint) -> CGFloat {
    return atan2(point.y - self.y, point.x - self.x)
}

func point(radians radians: CGFloat, withLength length: CGFloat) -> CGPoint   {
    return CGPoint(x: self.x + length * cos(radians), y: self.y + length * sin(radians))
}

func minus(point: CGPoint) -> CGPoint {
    return CGPoint(x: self.x - point.x, y: self.y - point.y)
}

func length() -> CGFloat {
    return sqrt(self.x * self.x + self.y + self.y)
}

}

class GameScene: SKScene {
var dBCircle : SKShapeNode!
let radiusDBCircle: CGFloat = 10
let radiusBall: CGFloat = 15
var balls = [SKShapeNode]()
var distanceBtwBalls : CGFloat = 15

private let SCALE_RATE: CGFloat = 0.3
override func didMoveToView(view: SKView) {
    // Some parameters
    let strokeColor = SKColor.orangeColor()
    let dBHeight = CGRectGetMaxY(self.frame)-84 // 64 navigationController height + 20 reasonable distance
    let dBStartX = CGRectGetMidX(self.frame)-160 // extreme left
    let dBStopX = CGRectGetMidX(self.frame)+160 // extreme right
    let dBWidth = dBStopX - dBStartX
    let totalBalls = 7 // first and last will be hidden
    let ballArea = dBWidth / CGFloat(totalBalls-1)
    distanceBtwBalls = ((ballArea-(radiusBall*2))+radiusBall*2)

    // Create dbCircle
    dBCircle = SKShapeNode.init(circleOfRadius: radiusDBCircle)
    dBCircle.position = CGPointMake(CGRectGetMidX(self.frame), dBHeight)
    dBCircle.strokeColor = strokeColor
    dBCircle.name = "dBCircle"
    dBCircle.fillColor = UIColor.clearColor()
    addChild(dBCircle)


    // Make static balls
    for i in 0..<totalBalls {
        let ball = SKShapeNode.init(circleOfRadius: radiusBall)
        ball.position =   CGPointMake(dBStartX+(distanceBtwBalls*CGFloat(i)), dBHeight)
        ball.strokeColor = strokeColor
        ball.name = "ball"
        ball.fillColor = UIColor.clearColor()
        balls.append(ball)
        if i == 0 || i == totalBalls-1 {
            ball.hidden = true
        }
        addChild(ball)
    }
    mediaTimingFunctionEaseInEaseOutEmulate(dBCircle,dBStartX: dBStartX,dBStopX: dBStopX)
}
func mediaTimingFunctionEaseInEaseOutEmulate(node:SKShapeNode,dBStartX:CGFloat,dBStopX:CGFloat) {
    let actionMoveLeft = SKAction.moveToX(dBStartX, duration:1.7)
    actionMoveLeft.timingMode = SKActionTimingMode.EaseInEaseOut

    let actionMoveRight = SKAction.moveToX(dBStopX, duration:1.7)
    actionMoveRight.timingMode = SKActionTimingMode.EaseInEaseOut

        node.runAction(SKAction.repeatActionForever(SKAction.sequence([actionMoveLeft,actionMoveRight])))
}
override func update(currentTime: NSTimeInterval) {
    var i = 0
    self.enumerateChildNodesWithName("ball") {
        node, stop in
        let ball = node as! SKShapeNode

        if CGRectContainsRect(ball.frame, self.dBCircle.frame) {
            if (ball.actionForKey("zoom") == nil) {

                let zoomIn = SKAction.scaleTo(1.5, duration: 0.25)
                let zoomOut = SKAction.scaleTo(1.0, duration: 0.25)
                let seq = SKAction.sequence([zoomIn,zoomOut])
                ball.runAction(seq,withKey: "zoom")
            }
        }
        i += 1
    }
    movingBeziers()
}

func _renderPath(path: UIBezierPath) {
    let shapeNode = SKShapeNode(path: path.CGPath)
    shapeNode.fillColor = UIColor.blueColor()
    addChild(shapeNode)

}

func movingBeziers()    {
    _renderPath(UIBezierPath(ovalInRect: dBCircle.frame))
    for j in 1..<balls.count {
    self.latestTestMetaball(j, circleShape: dBCircle, v: 0.6, handleLenRate:  2.0, maxDistance: self.distanceBtwBalls)
    }
}

func latestTestMetaball (j: Int, circleShape: SKShapeNode, v: CGFloat, handleLenRate: CGFloat, maxDistance: CGFloat)    {
    let circle1 = circleShape
    let circle2 = balls[j]

    let center1 = circle1.position
    let center2 = circle2.position

    let d = center1.distance(center2)

    var radius1 = circle1.frame.width
    var radius2 = circle2.frame.width


    var u1: CGFloat = 0.0
    var u2: CGFloat = 0.0
    if (d > maxDistance || d <= abs(radius1 - radius2)) {
        return
    } else if (d < radius1 + radius2) {
        u1 = acos((radius1 * radius1 + d * d - radius2 * radius2) / (2 * radius1 * d))
        u2 = acos((radius2 * radius2 + d * d - radius1 * radius1) / (2 * radius2 * d))
    } else {
        u1 = 0.0
        u2 = 0.0
    }

    let angle1 = center1.angleBetween(center2)
    let angle2 = acos((radius1 - radius2) / d)
    let angle1a = angle1 + u1 + (angle2 - u1) * v
    let angle1b = angle1 - u1 - (angle2 - u1) * v
    let angle2a = angle1 + CGFloat(M_PI) - u2 - (CGFloat(M_PI) - u2 - angle2) * v
    let angle2b = angle1 - CGFloat(M_PI) + u2 + (CGFloat(M_PI) - u2 - angle2) * v

    let p1a = center1.point(radians: angle1a, withLength: radius1)
    let p1b = center1.point(radians: angle1b, withLength: radius1)
    let p2a = center2.point(radians: angle2a, withLength: radius2)
    let p2b = center2.point(radians: angle2b, withLength: radius2)

    let totalRadius = radius1 + radius2
    var d2 = min(v * handleLenRate, p1a.minus(p2a).length() / totalRadius)
    d2 *= min(1, d * 2 / totalRadius)
    radius1 *= d2
    radius2 *= d2

    let cp1a = p1a.point(radians: angle1a - CGFloat(M_PI_2), withLength: radius1)
    let cp2a = p2a.point(radians: angle2a + CGFloat(M_PI_2), withLength: radius2)
    let cp2b = p2b.point(radians: angle2b - CGFloat(M_PI_2), withLength: radius2)
    let cp1b = p1b.point(radians: angle1b + CGFloat(M_PI_2), withLength: radius1)

    let pathJoinedCircles = UIBezierPath()
    pathJoinedCircles.moveToPoint(p1a)
    pathJoinedCircles.addCurveToPoint(p2a, controlPoint1: cp1a, controlPoint2: cp2a)
    pathJoinedCircles.addLineToPoint(p2b)
    pathJoinedCircles.addCurveToPoint(p1b, controlPoint1: cp2b, controlPoint2: cp1b)
    pathJoinedCircles.addLineToPoint(p1a)
    pathJoinedCircles.closePath()
    let shapeNode = SKShapeNode(path: pathJoinedCircles.CGPath)
    shapeNode.fillColor = UIColor.blueColor()
    addChild(shapeNode)
  }
}

解决方案

You can easily achieve this kind of animation using moveToX and the timingMode parameter.

New Swift 3 translation below at the end of this answer.

To make an example I use the Xcode Sprite-Kit "Hello, World!" official project demo:

class GameScene: SKScene {
    override func didMoveToView(view: SKView) {
        /* Setup your scene here */
        let myLabel = SKLabelNode(fontNamed:"Chalkduster")
        myLabel.text = "Hello, World!"
        myLabel.fontSize = 15
        myLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))

        self.addChild(myLabel)
        mediaTimingFunctionEaseInEaseOutEmulate(myLabel)
    }
    func mediaTimingFunctionEaseInEaseOutEmulate(node:SKLabelNode) {
        let actionMoveLeft = SKAction.moveToX(CGRectGetMidX(self.frame)-100, duration:1.5)
        actionMoveLeft.timingMode = SKActionTimingMode.EaseInEaseOut

        let actionMoveRight = SKAction.moveToX(CGRectGetMidX(self.frame)+100, duration:1.5)
        actionMoveRight.timingMode = SKActionTimingMode.EaseInEaseOut

        node.runAction(SKAction.repeatActionForever(SKAction.sequence([actionMoveLeft,actionMoveRight])))
    }
}

Output:

Update (This part start to emulate the static ball and the dynamic ball moving left and right but without metaball animations)

class GameScene: SKScene {
    var dBCircle : SKShapeNode!
    let radiusDBCircle: CGFloat = 10
    let radiusBall: CGFloat = 15
    private let SCALE_RATE: CGFloat = 0.3
    override func didMoveToView(view: SKView) {
        // Some parameters
        let strokeColor = SKColor.orangeColor()
        let dBHeight = CGRectGetMaxY(self.frame)-84 // 64 navigationController height + 20 reasonable distance
        let dBStartX = CGRectGetMidX(self.frame)-160 // extreme left
        let dBStopX = CGRectGetMidX(self.frame)+160 // extreme right
        let dBWidth = dBStopX - dBStartX
        let totalBalls = 7 // first and last will be hidden
        let ballArea = dBWidth / CGFloat(totalBalls-1)
        let distanceBtwBalls = ((ballArea-(radiusBall*2))+radiusBall*2)

        // Create dbCircle
        dBCircle = SKShapeNode.init(circleOfRadius: radiusDBCircle)
        dBCircle.position = CGPointMake(CGRectGetMidX(self.frame), dBHeight)
        dBCircle.strokeColor = strokeColor
        dBCircle.name = "dBCircle"
        dBCircle.fillColor = UIColor.clearColor()
        addChild(dBCircle)
        // Make static balls
        for i in 0..<totalBalls {
            let ball = SKShapeNode.init(circleOfRadius: radiusBall)
            ball.position =   CGPointMake(dBStartX+(distanceBtwBalls*CGFloat(i)), dBHeight)
            ball.strokeColor = strokeColor
            ball.name = "ball"
            ball.fillColor = UIColor.clearColor()
            if i == 0 || i == totalBalls-1 {
                ball.hidden = true
            }
            addChild(ball)
        }
        mediaTimingFunctionEaseInEaseOutEmulate(dBCircle,dBStartX: dBStartX,dBStopX: dBStopX)
    }
    func mediaTimingFunctionEaseInEaseOutEmulate(node:SKShapeNode,dBStartX:CGFloat,dBStopX:CGFloat) {
        let actionMoveLeft = SKAction.moveToX(dBStartX, duration:1.7)
        actionMoveLeft.timingMode = SKActionTimingMode.EaseInEaseOut

        let actionMoveRight = SKAction.moveToX(dBStopX, duration:1.7)
        actionMoveRight.timingMode = SKActionTimingMode.EaseInEaseOut

        node.runAction(SKAction.repeatActionForever(SKAction.sequence([actionMoveLeft,actionMoveRight])))
    }
    override func update(currentTime: NSTimeInterval) {
        var i = 0
        self.enumerateChildNodesWithName("ball") {
            node, stop in
            let ball = node as! SKShapeNode

            if CGRectContainsRect(ball.frame, self.dBCircle.frame) {
                if (ball.actionForKey("zoom") == nil) {
                    let zoomIn = SKAction.scaleTo(1.5, duration: 0.25)
                    let zoomOut = SKAction.scaleTo(1.0, duration: 0.25)
                    let seq = SKAction.sequence([zoomIn,zoomOut])
                    ball.runAction(seq,withKey: "zoom")
                }
            }
            i += 1
        }
    }
}

New update with metaball animation:

Finally I've realize this result, my goal is to make it very similar to the original :

is it possible to make some variations to times (for example zoomIn or zoomOut time values or actionMoveLeft, actionMoveRight time values), this is the code:

import SpriteKit
class GameScene: SKScene {
    var dBCircle : SKShapeNode!
    let radiusDBCircle: CGFloat = 10
    let radiusBall: CGFloat = 15
    private let SCALE_RATE: CGFloat = 0.3
    override func didMoveToView(view: SKView) {
        // Some parameters
        let strokeColor = SKColor.orangeColor()
        let dBHeight = CGRectGetMaxY(self.frame)-84 // 64 navigationController height + 20 reasonable distance
        let dBStartX = CGRectGetMidX(self.frame)-160 // extreme left
        let dBStopX = CGRectGetMidX(self.frame)+160 // extreme right
        let dBWidth = dBStopX - dBStartX
        let totalBalls = 7 // first and last will be hidden
        let ballArea = dBWidth / CGFloat(totalBalls-1)
        let distanceBtwBalls = ((ballArea-(radiusBall*2))+radiusBall*2)

        // Create dbCircle
        dBCircle = SKShapeNode.init(circleOfRadius: radiusDBCircle)
        dBCircle.position = CGPointMake(CGRectGetMidX(self.frame), dBHeight)
        dBCircle.strokeColor = strokeColor
        dBCircle.name = "dBCircle"
        dBCircle.fillColor = UIColor.clearColor()
        addChild(dBCircle)
        // Make static balls
        for i in 0..<totalBalls {
            let ball = SKShapeNode.init(circleOfRadius: radiusBall)
            ball.position =   CGPointMake(dBStartX+(distanceBtwBalls*CGFloat(i)), dBHeight)
            ball.strokeColor = strokeColor
            ball.name = "ball"
            ball.fillColor = UIColor.clearColor()
            if i == 0 || i == totalBalls-1 {
                ball.hidden = true
            }
            addChild(ball)
        }
        mediaTimingFunctionEaseInEaseOutEmulate(dBCircle,dBStartX: dBStartX,dBStopX: dBStopX)
    }
    func mediaTimingFunctionEaseInEaseOutEmulate(node:SKShapeNode,dBStartX:CGFloat,dBStopX:CGFloat) {
        let actionMoveLeft = SKAction.moveToX(dBStartX, duration:2.5)
        actionMoveLeft.timingMode = SKActionTimingMode.EaseInEaseOut

        let actionMoveRight = SKAction.moveToX(dBStopX, duration:2.5)
        actionMoveRight.timingMode = SKActionTimingMode.EaseInEaseOut

        node.runAction(SKAction.repeatActionForever(SKAction.sequence([actionMoveLeft,actionMoveRight])))
    }
    //MARK: - _metaball original function
    func _metaball(circle2:SKShapeNode, circle1:SKShapeNode, v: CGFloat, handeLenRate: CGFloat, maxDistance: CGFloat,vanishingTime : NSTimeInterval = 0.015) {
        let center1 = circle1.position
        let center2 = circle2.position
        let d = center1.distance(center2)
        var radius1 = radiusDBCircle
        var radius2 = radiusBall
        if (radius1 == 0 || radius2 == 0) {
            return
        }
        var u1: CGFloat = 0.0
        var u2: CGFloat = 0.0
        if (d > maxDistance || d <= abs(radius1 - radius2)) {
            return
        } else if (d < radius1 + radius2) {
            u1 = acos((radius1 * radius1 + d * d - radius2 * radius2) / (2 * radius1 * d))
            u2 = acos((radius2 * radius2 + d * d - radius1 * radius1) / (2 * radius2 * d))
        } else {
            u1 = 0.0
            u2 = 0.0
        }
        let angle1 = center1.angleBetween(center2)
        let angle2 = acos((radius1 - radius2) / d)
        let angle1a = angle1 + u1 + (angle2 - u1) * v
        let angle1b = angle1 - u1 - (angle2 - u1) * v
        let angle2a = angle1 + CGFloat(M_PI) - u2 - (CGFloat(M_PI) - u2 - angle2) * v
        let angle2b = angle1 - CGFloat(M_PI) + u2 + (CGFloat(M_PI) - u2 - angle2) * v
        let p1a = center1.point(radians: angle1a, withLength: radius1)
        let p1b = center1.point(radians: angle1b, withLength: radius1)
        let p2a = center2.point(radians: angle2a, withLength: radius2)
        let p2b = center2.point(radians: angle2b, withLength: radius2)
        let totalRadius = radius1 + radius2
        var d2 = min(v * handeLenRate, p1a.minus(p2a).length() / totalRadius)
        d2 *= min(1, d * 2 / totalRadius)
        radius1 *= d2
        radius2 *= d2
        let cp1a = p1a.point(radians: angle1a - CGFloat(M_PI_2), withLength: radius1)
        let cp2a = p2a.point(radians: angle2a + CGFloat(M_PI_2), withLength: radius2)
        let cp2b = p2b.point(radians: angle2b - CGFloat(M_PI_2), withLength: radius2)
        let cp1b = p1b.point(radians: angle1b + CGFloat(M_PI_2), withLength: radius1)
        let pathJoinedCircles = UIBezierPath()
        pathJoinedCircles.moveToPoint(p1a)
        pathJoinedCircles.addCurveToPoint(p2a, controlPoint1: cp1a, controlPoint2: cp2a)
        pathJoinedCircles.addLineToPoint(p2b)
        pathJoinedCircles.addCurveToPoint(p1b, controlPoint1: cp2b, controlPoint2: cp1b)
        pathJoinedCircles.addLineToPoint(p1a)
        pathJoinedCircles.closePath()
        let shapeNode = SKShapeNode(path: pathJoinedCircles.CGPath)
        shapeNode.strokeColor = SKColor.orangeColor()
        shapeNode.fillColor = UIColor.clearColor()
        addChild(shapeNode)
        let wait = SKAction.waitForDuration(vanishingTime)
        self.runAction(wait,completion: {
            shapeNode.removeFromParent()
        })
    }
    override func update(currentTime: NSTimeInterval) {
        var i = 0
        self.enumerateChildNodesWithName("ball") {
            node, stop in
            let ball = node as! SKShapeNode
            let enlargeFrame = CGRectMake(ball.frame.origin.x-self.radiusBall*3,ball.frame.origin.y,ball.frame.width+(self.radiusBall*6),ball.frame.height)
            if CGRectContainsRect(enlargeFrame, self.dBCircle.frame) {
                if (ball.actionForKey("zoom") == nil) {
                    let zoomIn = SKAction.scaleTo(1.5, duration: 0.25)
                    zoomIn.timingMode = SKActionTimingMode.EaseInEaseOut
                    let zoomOut = SKAction.scaleTo(1.0, duration: 0.25)
                    let wait = SKAction.waitForDuration(0.8)
                    let seq = SKAction.sequence([zoomIn,zoomOut,wait])
                    ball.runAction(seq,withKey: "zoom")
                }
            }
            self._metaball(ball, circle1: self.dBCircle, v: 0.6, handeLenRate: 2.0, maxDistance: 4 * self.radiusBall)
            i += 1
        }
    }
}
//MARK: - Extensions
extension CGPoint {
    func distance(point: CGPoint) -> CGFloat {
        let dx = point.x - self.x
        let dy = point.y - self.y
        return sqrt(dx * dx + dy * dy)
    }
    func angleBetween(point: CGPoint) -> CGFloat {
        return atan2(point.y - self.y, point.x - self.x)
    }
    func point(radians radians: CGFloat, withLength length: CGFloat) -> CGPoint   {
        return CGPoint(x: self.x + length * cos(radians), y: self.y + length * sin(radians))
    }
    func minus(point: CGPoint) -> CGPoint {
        return CGPoint(x: self.x - point.x, y: self.y - point.y)
    }
    func length() -> CGFloat {
        return sqrt(self.x * self.x + self.y + self.y)
    }
}

Swift 3:

(I've made a little change to maxDistance: 4 * self.radiusBall with maxDistance: 5 * self.radiusBall to become more similar to the original but you can change it as you wish)

import SpriteKit
class GameScene: SKScene {
    var dBCircle : SKShapeNode!
    let radiusDBCircle: CGFloat = 10
    let radiusBall: CGFloat = 15
    private let SCALE_RATE: CGFloat = 0.3
    override func didMove(to view: SKView) {
        let label = self.childNode(withName: "//helloLabel") as? SKLabelNode
        label?.removeFromParent()
        self.anchorPoint = CGPoint.zero
        // Some parameters
        let strokeColor = SKColor.orange
        let dBHeight = self.frame.midY
        let dBStartX = self.frame.midX-260 // extreme left
        let dBStopX = self.frame.midX+260 // extreme right
        let dBWidth = dBStopX - dBStartX
        let totalBalls = 7 // first and last will be hidden
        let ballArea = dBWidth / CGFloat(totalBalls-1)
        let distanceBtwBalls = ((ballArea-(radiusBall*2))+radiusBall*2)

        // Create dbCircle
        dBCircle = SKShapeNode.init(circleOfRadius: radiusDBCircle)
        dBCircle.position = CGPoint(x:self.frame.midX, y:dBHeight)
        dBCircle.strokeColor = strokeColor
        dBCircle.name = "dBCircle"
        dBCircle.fillColor = UIColor.clear
        addChild(dBCircle)
        // Make static balls
        for i in 0..<totalBalls {
            let ball = SKShapeNode.init(circleOfRadius: radiusBall)
            ball.position =   CGPoint(x:dBStartX+(distanceBtwBalls*CGFloat(i)), y:dBHeight)
            ball.strokeColor = strokeColor
            ball.name = "ball"
            ball.fillColor = UIColor.clear
            if i == 0 || i == totalBalls-1 {
                ball.isHidden = true
            }
            addChild(ball)
        }
        mediaTimingFunctionEaseInEaseOutEmulate(node: dBCircle,dBStartX: dBStartX,dBStopX: dBStopX)
    }
    func mediaTimingFunctionEaseInEaseOutEmulate(node:SKShapeNode,dBStartX:CGFloat,dBStopX:CGFloat) {
        let actionMoveLeft = SKAction.moveTo(x: dBStartX, duration:2.5)
        actionMoveLeft.timingMode = SKActionTimingMode.easeInEaseOut

        let actionMoveRight = SKAction.moveTo(x: dBStopX, duration:2.5)
        actionMoveRight.timingMode = SKActionTimingMode.easeInEaseOut

        node.run(SKAction.repeatForever(SKAction.sequence([actionMoveLeft,actionMoveRight])))
    }
    //MARK: - _metaball original function
    func _metaball(circle2:SKShapeNode, circle1:SKShapeNode, v: CGFloat, handeLenRate: CGFloat, maxDistance: CGFloat,vanishingTime : TimeInterval = 0.015) {
        let center1 = circle1.position
        let center2 = circle2.position
        let d = center1.distance(point: center2)
        var radius1 = radiusDBCircle
        var radius2 = radiusBall
        if (radius1 == 0 || radius2 == 0) {
            return
        }
        var u1: CGFloat = 0.0
        var u2: CGFloat = 0.0
        if (d > maxDistance || d <= abs(radius1 - radius2)) {
            return
        } else if (d < radius1 + radius2) {
            u1 = acos((radius1 * radius1 + d * d - radius2 * radius2) / (2 * radius1 * d))
            u2 = acos((radius2 * radius2 + d * d - radius1 * radius1) / (2 * radius2 * d))
        } else {
            u1 = 0.0
            u2 = 0.0
        }
        let angle1 = center1.angleBetween(point: center2)
        let angle2 = acos((radius1 - radius2) / d)
        let angle1a = angle1 + u1 + (angle2 - u1) * v
        let angle1b = angle1 - u1 - (angle2 - u1) * v
        let angle2a = angle1 + CGFloat(M_PI) - u2 - (CGFloat(M_PI) - u2 - angle2) * v
        let angle2b = angle1 - CGFloat(M_PI) + u2 + (CGFloat(M_PI) - u2 - angle2) * v
        let p1a = center1.point(radians: angle1a, withLength: radius1)
        let p1b = center1.point(radians: angle1b, withLength: radius1)
        let p2a = center2.point(radians: angle2a, withLength: radius2)
        let p2b = center2.point(radians: angle2b, withLength: radius2)
        let totalRadius = radius1 + radius2
        var d2 = min(v * handeLenRate, p1a.minus(point: p2a).length() / totalRadius)
        d2 *= min(1, d * 2 / totalRadius)
        radius1 *= d2
        radius2 *= d2
        let cp1a = p1a.point(radians: angle1a - CGFloat(M_PI_2), withLength: radius1)
        let cp2a = p2a.point(radians: angle2a + CGFloat(M_PI_2), withLength: radius2)
        let cp2b = p2b.point(radians: angle2b - CGFloat(M_PI_2), withLength: radius2)
        let cp1b = p1b.point(radians: angle1b + CGFloat(M_PI_2), withLength: radius1)
        let pathJoinedCircles = UIBezierPath()
        pathJoinedCircles.move(to: p1a)
        pathJoinedCircles.addCurve(to: p2a, controlPoint1: cp1a, controlPoint2: cp2a)
        pathJoinedCircles.addLine(to: p2b)
        pathJoinedCircles.addCurve(to: p1b, controlPoint1: cp2b, controlPoint2: cp1b)
        pathJoinedCircles.addLine(to: p1a)
        pathJoinedCircles.close()
        let shapeNode = SKShapeNode(path: pathJoinedCircles.cgPath)
        shapeNode.strokeColor = SKColor.orange
        shapeNode.fillColor = UIColor.clear
        addChild(shapeNode)
        let wait = SKAction.wait(forDuration: vanishingTime)
        self.run(wait,completion: {
            shapeNode.removeFromParent()
        })
    }
    override func update(_ currentTime: TimeInterval) {
        var i = 0
        self.enumerateChildNodes(withName: "ball") {
            node, stop in
            let ball = node as! SKShapeNode
            let enlargeFrame = CGRect(x:ball.frame.origin.x-self.radiusBall*3,y:ball.frame.origin.y,width:ball.frame.width+(self.radiusBall*6),height:ball.frame.height)
            if enlargeFrame.contains(self.dBCircle.frame) {
                if (ball.action(forKey: "zoom") == nil) {
                    let zoomIn = SKAction.scale(to: 1.5, duration: 0.25)
                    zoomIn.timingMode = SKActionTimingMode.easeInEaseOut
                    let zoomOut = SKAction.scale(to: 1.0, duration: 0.25)
                    let wait = SKAction.wait(forDuration: 0.7)
                    let seq = SKAction.sequence([zoomIn,zoomOut,wait])
                    ball.run(seq,withKey: "zoom")
                }
            }
            self._metaball(circle2: ball, circle1: self.dBCircle, v: 0.6, handeLenRate: 2.0, maxDistance: 5 * self.radiusBall)
            i += 1
        }
    }
}
//MARK: - Extensions
extension CGPoint {
    func distance(point: CGPoint) -> CGFloat {
        let dx = point.x - self.x
        let dy = point.y - self.y
        return sqrt(dx * dx + dy * dy)
    }
    func angleBetween(point: CGPoint) -> CGFloat {
        return atan2(point.y - self.y, point.x - self.x)
    }
    func point(radians: CGFloat, withLength length: CGFloat) -> CGPoint   {
        return CGPoint(x: self.x + length * cos(radians), y: self.y + length * sin(radians))
    }
    func minus(point: CGPoint) -> CGPoint {
        return CGPoint(x: self.x - point.x, y: self.y - point.y)
    }
    func length() -> CGFloat {
        return sqrt(self.x * self.x + self.y + self.y)
    }
}

这篇关于用SKScene和SKActions替换CALayer和CABasicAnimation的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆