I'm trying to smoothly animate a square into a circle in SpriteKit
.
I'm creating the SKShape
with a UIBezierPath
using rounded corners. Then, I vary the corner radii to animate.
My problem is that I seem to have a jump in the animation, please see the gif below. Preferably using the rounded corners technique, how can I get it to be smooth?
"Jumpy" problem
let shape = SKShapeNode()
let l: CGFloat = 100.0
shape.path = UIBezierPath(roundedRect: CGRect(x: -l/2, y: -l/2, width: l, height: l), byRoundingCorners: [.topLeft, .bottomLeft, .topRight, .bottomRight], cornerRadii: CGSize(width: 0, height: 0)).cgPath
shape.position = CGPoint(x: frame.midX, y: frame.midY)
shape.fillColor = .white
addChild(shape)
let action = SKAction.customAction(withDuration: 1) { (node, t) in
let shapeNode = node as! SKShapeNode
shapeNode.path = UIBezierPath(roundedRect: CGRect(x: -l/2, y: -l/2, width: l, height: l), byRoundingCorners: [.topLeft, .bottomLeft, .topRight, .bottomRight], cornerRadii: CGSize(width: t * l / 2, height: 0)).cgPath
}
shape.run(SKAction.repeatForever(action))
Animation debugging
To debug I have created some shapes with progressively larger corner radii as you can see below. The numbers represent the ratio of the corner radii to the length of the square. As you can see there is a jump between 0.3
and 0.35
. I can't see what I'm missing.
let cols = 10
let rows = 1
let l: Double = 30.0
let max: Double = l / 2
let delta: Double = l * 2
for i in 0..<rows * cols {
let s = SKShapeNode()
let c: Double = Double(i % cols)
let r: Double = floor(Double(i) / Double(cols))
let pct: Double = Double(i) / (Double(rows) * Double(cols))
let rad = pct * max
s.path = UIBezierPath(roundedRect: CGRect(x: -l/2, y: -l/2, width: l, height: l), byRoundingCorners: [.topRight, .bottomRight, .topLeft, .bottomLeft], cornerRadii: CGSize(width: pct * max, height: pct * max)).cgPath
s.position = CGPoint(x: c * delta - Double(cols) / 2.0 * delta, y: r * delta - Double(rows) / 2.0 * delta)
s.lineWidth = 1.5
s.strokeColor = .white
addChild(s)
let t = SKLabelNode(text: String(format:"%0.2f", rad / l))
t.verticalAlignmentMode = .center
t.horizontalAlignmentMode = .center
t.fontName = "SanFrancisco-Bold"
t.fontSize = 15
t.position = CGPoint(x: 0, y: -delta * 0.66)
s.addChild(t)
}
cornerRadii
: "Values larger than half the rectangle’s width or height are clamped appropriately to half the width or height.” Your formulawidth: t * l / 2, height: 0
seems odd... Indeed, since you are rounding all four corners of the bezier path, it is hard to see why you don’t just callinit(roundedRect:cornerRadius:)
. – mattt * l / 2
.t
goes linearly from 0 to 1 and this is multiplied by what I think the maximum value should be: the length (width) of the square divided by two. – Tibor Udvari