How to animate the opacity of a SKShapeNode? - swift

I am working on the instructions screen of my game and want to show to the user that they should tap a certain area of the screen.
So I want to show an animation where
1) A finger is scaling in and out of the screen, followed by -- working fine--
2) A rectangle changing opacity (to show to tap there) and then -- need help--
3) A text flashing saying, "Tap here" -- need help--
For 1), I have this (working fine):
finger = SKSpriteNode(texture: fingerTxt)
finger.position = CGPoint(x: 330, y: 450)
finger.zPosition = 10
InstHolderNode.addChild(finger)
let fingerTapScaleDown = SKAction.scale(by: 0.6, duration: 0.7)
let fingerTapScaleUp = SKAction.scale(by: 1.6, duration: 0.7)
let fingerScalingSequence = SKAction.sequence([fingerTapScaleDown,fingerTapScaleUp])
let fingerTapScaleForever = SKAction.repeatForever(fingerScalingSequence)
finger.run(fingerTapScaleForever)
For 2), I have :
var rect = SKShapeNode(rectOf: CGSize(width: 150.0, height: frame.height * 2))
rect.position = CGPoint(x: 300, y: 100)
rect.fillColor = SKColor.brown
rect.alpha = 0.5
InstHolderNode.addChild(rect)
Question :
How do I sync 1) and 2) such that after 1) is completed (the finger tapping animation is done), rect.alpha value to change to 0.1 and then changed back to 0.5 and then 1) happens and then rect.alpha is changed to change to 0.1 (in a loop) continuously.
Many thanks!!

Try this:
var fadeOut = SKAction.fadeAlpha(to: 0.1, duration: 0.5)
var fadeIn = SKAction.fadeAlpha(to: 0.5, duration: 0.5)
rect.run(SKAction.repeatForever(SKAction.sequence([fingerScalingSequence, fadeOut, fadeIn])))
Adjust the durations as per your liking

Related

animation not moving upwards in chaining animation in swift

I want my swift code to follow the gif I created. What is going is after the first animation there is not any change of the position. Like the gif I created it should first move down then move up again. I tried multiplying by the negative -0.15 thinking it would go in the north direction. Right now it is not having a effect.
UIView.animate(withDuration: 1, animations: {
self.block1.frame = CGRect(x: 0, y: self.view.frame.height * 0.15, width: self.view.frame.width, height: self.view.frame.height * 0.1)
self.block1.center = self.view.center
}) { done in
/// first animation finished, start second
if done {
UIView.animate(withDuration: 1, animations: {
self.block1.frame = CGRect(x: 0, y: self.view.frame.height * (-0.15), width: self.view.frame.width, height: self.view.frame.height * 0.1)
self.block1.center = self.view.center
})
}
}
Start by getting rid of self.block1.center = self.view.center as you're making the centre point of the block the same as the view in both cycles.
self.view.frame.height * (-0.15) is setting an absolute position out side of the view, not sure if that's intentional, but you might want to be aware of it. Instead, if you only wanted to move a given distance, say the height of the view, you should be using a relative position, such as block1.frame.origin.y -= block.frame.size.height (probably a much easier way to say that, but you get the idea)
So I created a really quick Playground, made up some values and got a "sort of" bouncy, returny thing going as an example
import UIKit
import XCPlayground
let view = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 300.0, height: 600.0))
// XCPShowView("container", view: container) // -> deprecated
let block1 = UIView(frame: CGRect(x: 0.0, y: 0.0, width: (view.frame.width) / 2, height: 150))
block1.backgroundColor = UIColor.green
block1.center.x = view.center.x
view.addSubview(block1)
UIView.animate(withDuration: 1, animations: {
let y = view.frame.height * 0.15
print(y)
block1.center.y = view.center.y
}) { done in
print("Done = \(done)")
/// first animation finished, start second
if done {
UIView.animate(withDuration: 1, animations: {
block1.frame.origin.y = 0
})
}
}
XCPlaygroundPage.currentPage.liveView = view
Caveat: There's probably a really neat and easy way to do "auto reversal" style animations using CALayer and I'd be keen to see other examples presenting the same concept

SWIFT problems with CGAffineTransform... button always go to origin

I got a problem with the animations from CGAffineTransform... I want to move the button and scale it up at the same time.
But if I do one animation alone everything works pretty straight forward. It scales up or moves the button by 50px.
But if I put both in the same animation its messed up. The button starts from far outside the screen to move in and doesn't scale as it should.
Also, after the movement of the button. The buttons scales up to the correct size i expected, but moves back to origin position before...
What do I miss here?
let shape = CAShapeLayer()
//shape.path = UIBezierPath(arcCenter: CGPoint(x: 25, y: 25), radius: 30, startAngle: 0, endAngle: .pi/2, clockwise: true).cgPath
shape.lineWidth = 5
shape.strokeColor = UIColor.black.cgColor
timerButton.layer.addSublayer(shape)
UIView.animate(withDuration: 2, animations: {
self.timerButton.transform = CGAffineTransform(translationX: -50, y: 0)
//self.timerButton.transform = CGAffineTransform(scaleX: 3, y: 3)
}) { (true) in
UIView.animate(withDuration: 1, animations: {
self.timerButton.transform = CGAffineTransform(scaleX: 3, y: 3)
}) { (true) in
print("later")
}
}
At completion of the transform of the x position, you need to change the constraint to make it permanent, the top constraint, after that also in the completion you need to remove the x position transformation,
If you follow these steps and after them animate your new transformation it should work

SKAction.move(to: ...) and SKAction.move(by: ...) does the exact same thing for me

I've set up an SKScene, that I fill with a bunch of blocks. Upon touch I want to move a random block to the 0,0 position of the SKScene. 0,0 is not the final goal, but I use it for test. My node is called a bixel and I naturally thought that using SKAction.move(to: ...) would move it to an absolute coordinate, as stated in the documentation, however I must be missing something, as moving to 0,0 does nothing, however moving to say 100,0 moves it 100 pixels to the right.
I've tried all the move(to: ...) (moveTo(x: ...) etc. And I've tried move(by: ...) which works as it should.
With the code provided below, if I switch between action and action2 (by rebuilding the project), the outcome is always the same, it's moved 100 pixels to the right.
I've also tried the point convert:
let point = self.convert(CGPoint(x: 0, y: 0), to: bixel)
but the result is the same.
let bixel = bixels[Int.random(in: 0..<bixels)] bixel.fillColor = .red
let action = SKAction.move(to: CGPoint(x: 100, y: 0), duration: 0.1)
let action2 = SKAction.move(by: CGVector(dx: 100, dy: 0), duration: 0.1)
bixel.zPosition = 1000
bixel.run(action)
EDIT: You are completely correct #Martin R. And as I wrote in my comment to your answer every ShapeNode thought that their position was 0,0. It was because of how I initialised them.
I did this:
let bixel = SKShapeNode(
rect: CGRect(
x: xOffset + (xF * shapeSize.width),
y: self.size.height - yOffset - (yF * shapeSize.height),
width: shapeSize.width - space,
height: shapeSize.height - space
),
cornerRadius: 2
)
Which made it think it's position was 0,0. So when I just changed it to this:
let bixel = SKShapeNode(rectOf: shapeSize, cornerRadius: 2)
bixel.position = CGPoint(
x: xOffset + (xF * shapeSize.width),
y: self.size.height - yOffset - (yF * shapeSize.height)
)
everything worked. Thanks!
SKAction.move(to:duration:) creates an action that moves a node to the given position.
SKAction.move(to: CGPoint(x: 100, y: 0), duration: 0.1)
creates an action that moves a node to position (100, 0).
SKAction.move(by:duration:) creates an action that moves a node relative to its current position.
SKAction.move(by: CGVector(dx: 100, dy: 0), duration: 0.1)
creates an action that moves a node from its current position (x0, y0) to the new position (x0+100, y0).
Both actions have the same effect if (and only if) the current node position is (0, 0).

I'm trying to create an infinite vertical scrolling background with SpirteKit, however I can't seem to get the code to work

The loop isn't timed correctly and resets too quickly. The looped image is also cut off showing only a fraction of it rather than the full looped background image stitched to the previous one.
I've attempted playing around with the .position, .size, and the SKAction values, but I just can't seem to tweak it to where it works properly.
func scrollingBackground() {
let BGTexture = SKTexture(imageNamed: "Background")
for i in 0 ... 1 {
let background = SKSpriteNode(texture: BGTexture)
background.zPosition = -30
background.anchorPoint = CGPoint(x: 0, y: 0)
background.size = CGSize(width: (frame.size.width), height: (frame.size.height))
background.position = CGPoint(x: 0, y: -BGTexture.size().height + (BGTexture.size().height + (BGTexture.size().height * CGFloat(i))))
addChild(background)
let scrollUp = SKAction.moveBy(x: 0, y: BGTexture.size().height, duration: 20)
let scrollReset = SKAction.moveBy(x: 0, y: -BGTexture.size().height, duration: 0)
let scrollLoop = SKAction.sequence([scrollUp, scrollReset])
let scrollForever = SKAction.repeatForever(scrollLoop)
background.run(scrollForever)
}
I hoping to get a seamless infinite background loop that runs indefinitely rather than this jumpy mess I currently have. I might add that my background image isn't originally sized perfectly to the devices, but I'm wanting to make sure my background fits to all iPhone devices.
Your first BG is at y = 0, Your second BG is at y = height. I am assuming that you are on anchor point 0.5 0.5, which would make height / 2 off screen. Your action moves BG 1 exactly to where it is off screen, then bumps back to 0. Your BG2 starts off screen, continues to move off screen, then when it hits 2 * height, resets to 1 * height, thus never appears on the screen. This is going to give the appearance of a black bar at the bottom, because BG2 is never drawn. Multiply your i by negative height instead, and that should cause the BG2 to appear on the bottom instead.
func scrollingBackground() {
let BGTexture = SKTexture(imageNamed: "Background")
for i in 0 ... 1 {
let background = SKSpriteNode(texture: BGTexture)
background.zPosition = -30
background.anchorPoint = CGPoint(x: 0, y: 0)
background.size = CGSize(width: (frame.size.width), height: (frame.size.height))
background.position = CGPoint(x: 0, y: -BGTexture.size().height + (BGTexture.size().height + (-BGTexture.size().height * CGFloat(i))))
addChild(background)
let scrollUp = SKAction.moveBy(x: 0, y: BGTexture.size().height, duration: 20)
let scrollReset = SKAction.moveBy(x: 0, y: -BGTexture.size().height, duration: 0)
let scrollLoop = SKAction.sequence([scrollUp, scrollReset])
let scrollForever = SKAction.repeatForever(scrollLoop)
background.run(scrollForever)
}

SKSpriteNode animation to appear bottom-up

According to the following code, i am displaying a mountain (hill) in the scene at x = 400 and y = self.frame.height / 2
However, I want the hill to appear from bottom-up (from below the scene) like an animation. How can I code this ?
let hill = SKSpriteNode(imageNamed: "hill")
hill.position = CGPoint(x: 400, y: self.frame.height / 2 )
hill.setScale(1)
hill.physicsBody = SKPhysicsBody(rectangleOfSize: hill.size)
hill.physicsBody?.categoryBitMask = 1
hill.physicsBody?.collisionBitMask = 2
self.addChild(hill)
To create an animation that makes the sprite slide up from "under" the scene, do this:
Set its initial position to below the scene:
hill.position = CGPoint(x: 400, y: self.frame.height / CGFloat(2) - self.frame.height)
Note: - self.frame.height is used to place the node 1 full frame height below the desired location
Then start an action that moves it up to its desired position in didMoveToView, or wherever you want the animation to start:
let moveUpAnimation = SKAction.move(to: CGPoint(x: 400, y: self.frame.height / CGFloat(2)), duration: 0.75)
hill.run(moveUpAnimation)
This example creates an action that takes 0.75 seconds to move the sprite up one full frame height. You can set its initial position to anywhere below the visible scene to create this effect, and change the duration to any desired speed.