Swift, sprite kit game: Have circle disappear in clockwise manner? On timer?
By changing the path
property of an SKShapeNode
at a fixed interval, you can create a frame-by-frame animation sequence. To create the animation, set the path
property to a sequence of shapes that starts with a circle and ends with nothing. You can use UIBezierPath, a wrapper for CGPath
, to create shapes for the animation using the following steps:
- Move path's "pen" to the center of the circle
- Add an arc to the path with addArcWithCenter from a
startAngle
toendAngle
- Add a line to the path from the point on the circle corresponding to the ending angle to the center
- Change the
endAngle
by a fixed amount - Repeat steps 1-4
Here's an implementation of the above steps:
override func didMove(to:SKView) {
let circle = SKShapeNode(circleOfRadius: 50)
circle.fillColor = SKColor.blue
circle.strokeColor = SKColor.clear
circle.zRotation = CGFloat.pi / 2
addChild(circle)
countdown(circle: circle, steps: 20, duration: 5) {
print("done")
}
}
// Creates an animated countdown timer
func countdown(circle:SKShapeNode, steps:Int, duration:TimeInterval, completion:@escaping ()->Void) {
guard let path = circle.path else {
return
}
let radius = path.boundingBox.width/2
let timeInterval = duration/TimeInterval(steps)
let incr = 1 / CGFloat(steps)
var percent = CGFloat(1.0)
let animate = SKAction.run {
percent -= incr
circle.path = self.circle(radius: radius, percent:percent)
}
let wait = SKAction.wait(forDuration:timeInterval)
let action = SKAction.sequence([wait, animate])
run(SKAction.repeat(action,count:steps-1)) {
self.run(SKAction.wait(forDuration:timeInterval)) {
circle.path = nil
completion()
}
}
}
// Creates a CGPath in the shape of a pie with slices missing
func circle(radius:CGFloat, percent:CGFloat) -> CGPath {
let start:CGFloat = 0
let end = CGFloat.pi * 2 * percent
let center = CGPoint.zero
let bezierPath = UIBezierPath()
bezierPath.move(to:center)
bezierPath.addArc(withCenter:center, radius: radius, startAngle: start, endAngle: end, clockwise: true)
bezierPath.addLine(to:center)
return bezierPath.cgPath
}
and a video clip:
Swift: how to invalidate timer on animated SKShapenode countdown?
You can stop an action by adding a key to the action and then run
removeActionForKey("actionKey")
To add a key to the action, replace the countdown
method with the following:
// Creates an animated countdown timer
func countdown(circle:SKShapeNode, steps:Int, duration:NSTimeInterval, completion:()->Void) {
let radius = CGPathGetBoundingBox(circle.path).width/2
let timeInterval = duration/NSTimeInterval(steps)
let incr = 1 / CGFloat(steps)
var percent = CGFloat(1.0)
let animate = SKAction.runBlock({
percent -= incr
circle.path = self.circle(radius, percent:percent)
})
let wait = SKAction.waitForDuration(timeInterval)
let action = SKAction.sequence([wait, animate])
let completed = SKAction.runBlock{
circle.path = nil
completion()
}
let countDown = SKAction.repeatAction(action,count:steps-1)
let sequence = SKAction.sequence([countDown, SKAction.waitForDuration(timeInterval),completed])
runAction(sequence, withKey: "actionKey")
}
Drawing circle outline with small gap Sprite Kit Swift
You can get something similar using the SKShapeNode
in combination with SKEffectNode
, like this:
override func didMoveToView(view: SKView) {
let effectNode = SKEffectNode()
let gap = CGFloat(M_PI_4 / 4.0)
for i in 0...3 {
let shape = SKShapeNode(circleOfRadius: 50)
shape.fillColor = .clearColor()
shape.lineWidth = 6.8
shape.strokeColor = .darkGrayColor()
let startAngle:CGFloat = CGFloat(i) * CGFloat(M_PI_2) + gap
let endAngle:CGFloat = startAngle + CGFloat(M_PI_2) - gap * 2
print("Iteration \(i) : start angle (\(startAngle * 180 / CGFloat(M_PI)), end angle (\(endAngle * 180 / CGFloat(M_PI)))")
shape.path = UIBezierPath(arcCenter: CGPointZero, radius: -50, startAngle: startAngle, endAngle: endAngle, clockwise: true).CGPath
effectNode.addChild(shape)
}
effectNode.position = CGPoint(x: frame.midX, y: frame.midY)
effectNode.shouldRasterize = true
addChild(effectNode)
}
The result:
Or you could make a mask programatically and apply it to a SKCropNode
. The SKCropNode of course will be a parent of a ring (SKShapeNode
with fillColor
set to .clearColor()
and strokeColor
set to appropriate value).
Sprite Kit - Draw circle with muilticolored borders
Import the object into your game as a png (from photoshop or whatever) and when a node collides with it, detect the point it contacts and calculate the difference in x and y from the center of the circle. Even if the circle is rotating, this should always tell you what color the colliding object hit, as long as you have the colors mapped to "zones" already. make sense?
Alternatively, you could create a transparent circle (SKShapeNode) and have eight SKShapeNode arcs as children of that transparent circle. Position them deliberately. This adds the benefit that each will be a separate object, can have its own strokeColor, and its own touch methods. They would need to be collision-able while the parent transparent circle would need to ignore collisions for it to work.
As for how to detect which section of the circle was hit, here is how I would do it:
Given a circle that is divided into 8 equally sized pie pieces, you should be able to determine which section received a collision based on the x and y values of the collision point compared to the center of the circle. For example, assuming that the Y axis is a division between two halves of four sections each (see pic), you can first narrow the collision down by quadrant as follows:
cx, cy = collision x and y values
ox, oy = the x and y values of the very center (origin) of the circle
pseudocode:
if cx > ox && cy > oy, then the collision occurred in quadrant 1
if cx > ox && cy < oy, then the collision occurred in quadrant 2
if cx < ox && cy < oy, then the collision occurred in quadrant 3
if cx < ox && cy > oy, then the collision occurred in quadrant 4
Since there are two sections in each quadrant you can further refine this logic, determining which section of the two the collision occurred in by comparing the difference
pseudocode:
if the difference between cx and ox > the difference between cy and oy, then the collision occurred in Section 2
else the collision occurred in section 1
Combining these logics into one nested if-else statement you'd have something like this:
pseudocode:
if cx > ox && cy > oy, then the collision occurred in quadrant 1
......if the difference between cx and ox > the difference between cy and oy, then the collision occurred in Section 2
......else the collision occurred in section 1
if cx > ox && cy < oy, then the collision occurred in quadrant 2
......if the difference between cx and ox > the difference between cy and oy, then the collision occurred in Section 3
......else the collision occurred in section 4
if cx < ox && cy < oy, then the collision occurred in quadrant 3
......if the difference between cx and ox > the difference between cy and oy, then the collision occurred in Section 6
......else the collision occurred in section 5
if cx < ox && cy > oy, then the collision occurred in quadrant 4
......if the difference between cx and ox > the difference between cy and oy, then the collision occurred in Section 7
......else the collision occurred in section 8
HTH!
Passenger/Apache: Can't set expire headers for versioned resources (rewrite rule not recognized)
I prefer to do this by combining it with using an assets host on a separate subdomain to avoid the whole rewrite issue. That way you can set the expire headers for everything on that subdomain. You can activate this in rails in environments/production.rb.
If you don't want to go with a separate subdomain I think the code below should do it, although I have not tested it myself:
ExpiresActive On
<FilesMatch "\.(ico|gif|jpe?g|png|js|css)$">
ExpiresDefault "access plus 1 year"
</FilesMatch>
Related Topics
Wrapping a Generic Method in a Class Extension
Swift Protocol to Require Properties as Protocol
If the Swift 'Guard' Statement Must Exit Scope, What Is the Definition of Scope
Swiftui Label Text and Image Vertically Misaligned
Naming Convention for Optional Binding
How to Convert Hexstring to Bytearray in Swift 3
How to Check If a Property Has Been Set Using Swift Reflection
How to Make My Level Menu Scrollable Vertically
Output Compile Durations for Swift Files
How to Change the Current Day's Hours and Minutes in Swift
Add Skreferencenode/Skscene to Another Skscene in Spritekit
Alamofire Type 'Parameterencoding' Has No Member 'Url' Swift 3
How to Avoid That My Swift Async Method Runs on the Main Thread in Swiftui
How Are Hash Collisions Handled
How to Send Push Notifications Without Using Firebase Console