Fading action of SKNode() with SKCropNode() children - swift

I have a SKNode() which has SKCropNode() children. I am able to run all kind of actions to rotate and scale my SKNode(), but when I want to use any kind of fading actions the result always is alpha 1 or alpha 0. No way of fading.
By replacing the SKCropNode() with a SKShapeNode() the fading action is working fine.
Can anyone tell my why? Isn't it possible to fade masked nodes?
import SpriteKit
class GameScene: SKScene {
let player = SKNode()
let playerSize = CGFloat(50)
let playerCrop = SKCropNode()
let playerMask = SKSpriteNode(color: SKColor.blackColor(), size: CGSizeMake(CGFloat(100), CGFloat(100)))
let playerCircle = SKShapeNode(circleOfRadius: CGFloat(100))
let playerCenterMask = SKShapeNode(circleOfRadius: CGFloat(100))
let playerCenterCrop = SKCropNode()
override func didMoveToView(view: SKView) {
backgroundColor = SKColor.blackColor()
player.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
player.alpha = 0
addChild(player)
playerCenterMask.lineWidth = 20
playerCenterCrop.maskNode = playerCenterMask
playerMask.position.x = -playerSize
playerMask.position.y = playerSize
playerCircle.lineWidth = 0
playerCircle.fillColor = SKColor.redColor()
playerCrop.addChild(playerCircle)
playerCrop.maskNode = playerMask
playerCenterCrop.addChild(playerCrop)
player.addChild(playerCenterCrop)
// Animations
let playerScale = SKAction.scaleTo(2.0, duration: 5)
let playerFadeIn = SKAction.fadeInWithDuration(5)
let playerAnimation = SKAction.group([playerScale,playerFadeIn])
player.runAction(playerAnimation, completion: {})
}
}

As mentioned in the comments:
SKCropNode uses alpha < 0.5 to not draw. >= 0.5 to draw, and you can't set the blend mode, so it is probably doing source blend mode. Which means it is overwriting the alpha. Children afterwards get blended.
The player context is created, it draws at the given alpha, then the SKCropNode is drawn, overwriting alpha instead of blending.
Run the fade in action on your child, not your parent to get the results you are looking for.
Here is what your source looks like with changes:
import SpriteKit
class GameScene: SKScene {
let player = SKNode()
let playerSize = CGFloat(50)
let playerCrop = SKCropNode()
let playerMask = SKSpriteNode(color: SKColor.blackColor(), size: CGSizeMake(CGFloat(100), CGFloat(100)))
let playerCircle = SKShapeNode(circleOfRadius: CGFloat(100))
let playerCenterMask = SKShapeNode(circleOfRadius: CGFloat(100))
let playerCenterCrop = SKCropNode()
override func didMoveToView(view: SKView) {
backgroundColor = SKColor.blackColor()
player.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
player.alpha = 1
addChild(player)
playerCircle.alpha = 0
playerCenterMask.lineWidth = 20
playerCenterCrop.maskNode = playerCenterMask
playerMask.position.x = -playerSize
playerMask.position.y = playerSize
playerCircle.lineWidth = 0
playerCircle.fillColor = SKColor.redColor()
playerCrop.addChild(playerCircle)
playerCrop.maskNode = playerMask
playerCenterCrop.addChild(playerCrop)
player.addChild(playerCenterCrop)
// Animations
let playerScale = SKAction.scaleTo(2.0, duration: 5)
let playerFadeIn = SKAction.fadeInWithDuration(5)
let playerAnimation = playerScale
player.runAction(playerAnimation, completion: {})
playerCircle.runAction(playerFadeIn, completion: {})
}
}

Related

Swift: Player Jump Animation

I'm trying to get my player to play the jump animation when the touch begins. The player is in his own swift file and I have the GameScene.swift call his file so everything that deals with his movement is in player.swift file. Any help would be awesome!
import SpriteKit
struct ColliderType {
static let PLAYER: UInt32 = 0
static let GROUND: UInt32 = 1
static let ROCKET_AND_COLLECTABLES: UInt32 = 2
}
class Player: SKSpriteNode {
private var textureAtlas = SKTextureAtlas()
private var playerAnimation = [SKTexture]()
private var animatePlayerAction = SKAction()
let jumpAnimation1 = [SKTexture]()
let jumpAnimation2 = [SKTexture]()
var jumpAnimation = SKAction ()
func initializePlayer() {
name = "Player"
for i in 1...7 {
let name = "Player \(i)"
playerAnimation.append(SKTexture(imageNamed: name))
}
animatePlayerAction = SKAction.animate(with: playerAnimation,
timePerFrame: 0.08, resize: true, restore: false)
//Animate the player forever.
self.run(SKAction.repeatForever(animatePlayerAction))
physicsBody = SKPhysicsBody(rectangleOf: CGSize(width:
self.size.width-60, height: self.size.height-80))
physicsBody?.affectedByGravity = true
physicsBody?.allowsRotation = false
//Restitution is Boucniess
physicsBody?.restitution = 0
physicsBody?.categoryBitMask = ColliderType.PLAYER
physicsBody?.collisionBitMask = ColliderType.GROUND
physicsBody?.contactTestBitMask =
ColliderType.ROCKET_AND_COLLECTABLES
}
func move (){
self.position.x += 10
}
func reversePlayer() {
physicsBody?.velocity = CGVector(dx: 0, dy: 0)
physicsBody?.applyImpulse(CGVector(dx: 0, dy: 300))
playerNode?.removeAction(forKey:"animate") playerNode?.run(attackAnimation,completion:{
self.playerNode?.run(self.animation,withKey: "animate")
}
}
Replace the part where you set textures, create and run the action with the following code snippet. It works perfectly for me and is the simplest way to do it.
let hero = SKSpriteNode(imageNamed: "hero")
hero.size = CGSize(width: 45, height: 125)
let f1 = SKTexture.init(imageNamed: "h1")
let f2 = SKTexture.init(imageNamed: "h2")
let f3 = SKTexture.init(imageNamed: "h3")
let frames: [SKTexture] = [f1, f2, f3]
let hero = SKSpriteNode(imageNamed: "h1")
let animation = SKAction.animate(with: frames, timePerFrame: 0.1)
hero.run(SKAction.repeatForever(animation))
Also, it is possible that your animation action cannot be accessed as it is in another file. Declare the player and animations globally i.e. outside of any class or function. Hope it works for you!

How to make SKSpriteNode fall at a downward curve

What I have Now: The node currently only travels in a straight line on the x-axis.
let actionBomb = SKAction.repeatForever(SKAction.moveTo(x: 495, duration: 3.0))
What I’m looking to achieve: The goal is to have the node travel at a downward curve until hitting the ground. I’m not sure how to go about achieving this.
Here is my current code:
func bombAction(){
if(self.action(forKey: "spawnBomb") != nil){return}
let bombTimer = SKAction.wait(forDuration: 5, withRange: 8)
let spawnbombA = SKAction.run {
self.bomb = SKSpriteNode(imageNamed: "bomb1")
self.bomb.zPosition = 1
self.bomb.physicsBody = SKPhysicsBody(circleOfRadius: self.bomb.size.height / 10.0)
self.bomb.physicsBody?.isDynamic = true
self.bomb.physicsBody?.allowsRotation = false
self.bomb.physicsBody?.affectedByGravity = true
var bombPosition = Array<CGPoint>()
bombPosition.append((CGPoint(x:400, y:475)))
bombPosition.append((CGPoint(x:500, y:475)))
bombPosition.append((CGPoint(x:750, y:475)))
let shuffledLocations = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: bombPosition) as! [CGPoint]
self.bomb.position = shuffledLocations[0]
let spawnLocationBomb = bombPosition[Int(arc4random_uniform(UInt32(bombPosition.count)))]
let actionBomb = SKAction.repeatForever(SKAction.moveTo(x: 495, duration: 3.0))
self.bomb.run(actionBomb)
self.bomb.position = spawnLocationBomb
self.addChild(self.bomb)
print(spawnLocationBomb)
}
let sequenceBomb = SKAction.sequence([bombTimer, spawnbombA])
self.run(SKAction.repeatForever(sequenceBomb), withKey: "spawnBomb")
}
Don't forget to set gravity for your scene's physicsWorld
class GameScene: SKScene {
override func didMove(to view: SKView) {
physicsWorld.gravity = CGVector(dx: 0, dy: -1)
}
}

Assign Multiple Masks to SKCropNode

As an example, I have a circle which travels to the left and then the right on the screen. I want the circle to only be visible if it is inside two specific squares (maskNodes). I'm using SKCropNode to try and achieve this, but the SKCropNode mask only lets me assign one mask. Does anybody know a way of assigning two or more mask to a SKCropNode, or if it is even possible to do so. Thanks!
override func didMoveToView(view: SKView) {
anchorPoint = CGPointMake(0.5, 0.5)
backgroundColor = UIColor.whiteColor()
let mask1 = SKSpriteNode(color: UIColor.blackColor(), size: CGSizeMake(50, 50))
mask1.position.x = -100
let mask2 = SKSpriteNode(color: UIColor.blackColor(), size: CGSizeMake(50, 50))
mask2.position.x = 100
let cropNode = SKCropNode()
cropNode.maskNode = mask1 // && mask2
addChild(cropNode)
let circle = SKShapeNode(circleOfRadius: 25)
circle.fillColor = UIColor.blackColor()
cropNode.addChild(circle)
// Move Circle
let moveLeft = SKAction.moveToX(-frame.size.width/2, duration: 2)
let moveRight = SKAction.moveToX(frame.size.width/2, duration: 2)
let seq = SKAction.repeatActionForever(SKAction.sequence([moveLeft, moveRight]))
circle.runAction(seq)
}
Figured it out, just has to add the two mask to a parent and then assign the parent as the SKCropNode mask.
override func didMoveToView(view: SKView) {
anchorPoint = CGPointMake(0.5, 0.5)
backgroundColor = UIColor.whiteColor()
let maskParent = SKSpriteNode()
let mask1 = SKSpriteNode(color: UIColor.blackColor(), size: CGSizeMake(50, 50))
mask1.position.x = -100
maskParent.addChild(mask1)
let mask2 = SKSpriteNode(color: UIColor.blackColor(), size: CGSizeMake(50, 50))
mask2.position.x = 100
maskParent.addChild(mask2)
let cropNode = SKCropNode()
cropNode.maskNode = maskParent
addChild(cropNode)
let circle = SKShapeNode(circleOfRadius: 25)
circle.fillColor = UIColor.blackColor()
cropNode.addChild(circle)
// Move Circle
let moveLeft = SKAction.moveToX(-frame.size.width/2, duration: 2)
let moveRight = SKAction.moveToX(frame.size.width/2, duration: 2)
let seq = SKAction.repeatActionForever(SKAction.sequence([moveLeft, moveRight]))
circle.runAction(seq)
}

How can I add a physics body to an SKAtlasTexture or create an animation through Images.xcassets?

I wanted to create a small animation of a car driving down the road, so I made an atlas of 9 different pictures. The car simply looks like its wheels are rotating and the car is bouncing a bit as it drives along. I already made an SKSpriteNode with an image and added a physics body on it so that it can jump and be affected by gravity.
So I was wondering how to add either a physics body to an SKAtlasTexture or create an animation through my image.xcassets folder. I tried to just change the SKSpriteNode to SKAtlasTexture, but that obviously didn't work as there are no physics bodies in SKAtlasTexture. So that's where I'm at. Any suggestions or solutions would be greatly appreciated.
Here some parts of my code:
class PlayScene: SKScene, SKPhysicsContactDelegate {
let road = SKSpriteNode(imageNamed: "road")
var origRoadPositionX = CGFloat(0)
var maxRoad = CGFloat(0)
var groundSpeed = 3
var carBaseLine = CGFloat(0)
let car = SKSpriteNode(imageNamed: "car")
enum ColliderType:UInt32{
case car = 1
case tower = 2
}
override func didMoveToView(view: SKView) {
self.backgroundColor = UIColor(hex: 0x80E8FF)
self.physicsWorld.contactDelegate = self
//Car
self.car.position = CGPointMake(CGRectGetMinX(self.frame)-20 + self.car.size.width, self.carBaseLine)
self.car.physicsBody = SKPhysicsBody (rectangleOfSize: self.car.size)
self.car.physicsBody?.allowsRotation = false
self.car.physicsBody?.affectedByGravity = false
self.car.physicsBody?.categoryBitMask = ColliderType.car.rawValue
self.car.physicsBody?.contactTestBitMask = ColliderType.tower.rawValue
self.car.physicsBody?.collisionBitMask = ColliderType.tower.rawValue
self.addChild(car)
If more code is needed in order to find a solution, let me know and i can supply more of it.
You can use atlas folder for performing animation with images.
Consider below example:
import SpriteKit
class GameScene: SKScene {
var bombFrames : [SKTexture]!
var bomb : SKSpriteNode!
let NoneCategory : UInt32 = 0x1 << 0
let ProjectileCategory : UInt32 = 0x1 << 2
let bombCategory : UInt32 = 0x1 << 7
override func didMoveToView(view: SKView) {
/* Setup your scene here */
runAction(SKAction.repeatActionForever(SKAction.sequence([SKAction.runBlock(addBomb), SKAction.waitForDuration(5.0)])))
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
func addBomb() {
let Name = "Bomb"
let AnimatedAtlas = SKTextureAtlas(named: Name)
var Framese = [SKTexture]()
let numImages = AnimatedAtlas.textureNames.count
for var i=1; i<=numImages; i++ {
let TextureName = "\(i)"
Framese.append(AnimatedAtlas.textureNamed(TextureName))
}
bombFrames = Framese
let firstFrame = bombFrames[0]
bomb = SKSpriteNode(texture: firstFrame)
let actualY = random(min: bomb.size.height/2, max: size.height - bomb.size.height/2)
bomb.position = CGPoint(x: size.width + bomb.size.width/2, y: actualY)
bomb.physicsBody = SKPhysicsBody(texture: bomb.texture, size: bomb.texture!.size())
bomb.physicsBody?.dynamic = true
bomb.physicsBody?.categoryBitMask = bombCategory
bomb.physicsBody?.contactTestBitMask = ProjectileCategory
bomb.physicsBody?.collisionBitMask = NoneCategory
bomb.physicsBody?.usesPreciseCollisionDetection = true
addChild(bomb)
let actualDuration = random(min: CGFloat(2.0), max: CGFloat(4.0))
let actionMove = SKAction.moveTo(CGPoint(x: -bomb.size.width/2, y: actualY), duration: NSTimeInterval(actualDuration))
let actionMoveDone = SKAction.removeFromParent()
bomb.runAction(SKAction.sequence([actionMove, actionMoveDone]))
playBombAnimation()
}
func random() -> CGFloat {
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}
func random(#min: CGFloat, max: CGFloat) -> CGFloat {
return random() * (max - min) + min
}
func playBombAnimation() {
bomb.runAction(SKAction.repeatActionForever(SKAction.animateWithTextures(bombFrames, timePerFrame: 0.1, resize: false, restore: true)), withKey:"bombAnimation")
}
}
And don't forget to add atlas folder into your project navigator like this:
As you can see in code you can add physics body to your sprite. and if you want you can try this way.
Hope this will help.

SKLabel won't show up

Im making a flappy bird type game in swift using sprite kit. The game works fine but I'm trying to make a way to score the game so I set up a variable and SKLabel that adds on to itself whenever a pipe is about half way across the screen. However I cannot get the label to show up any help doing this here is my code:
// flappy rainbow sheep
//
// Created by Heather Arnold on 3/3/15.
// Copyright (c) 2015 ian arnold. All rights reserved.
//
import SpriteKit
class GameScene: SKScene {
var bird = SKSpriteNode()
var pipeUpTexture = SKTexture()
var pipeDownTexture = SKTexture()
var PipeMoveAndRemove = SKAction()
let pipeGap = 225.0
var score: Int = 0
var scoreLabel: SKLabelNode = SKLabelNode(fontNamed:"System-Bold")
override func didMoveToView(view: SKView) {
/* Setup your scene here */
score = 0
//Physics
self.physicsWorld.gravity = CGVectorMake(0.0, -10.0);
//Bird
var BirdTexture = SKTexture(imageNamed:"flappysheep")
BirdTexture.filteringMode = SKTextureFilteringMode.Nearest
bird = SKSpriteNode(texture: BirdTexture)
bird.setScale(0.5)
bird.position = CGPoint(x: self.frame.size.width * 0.35, y: self.frame.size.height * 0.6)
scoreLabel.position.x = 50
scoreLabel.position.y = view.bounds.height - 50
scoreLabel.text = "0"
scoreLabel.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Left
scoreLabel.hidden = false
self.addChild(scoreLabel)
bird.physicsBody = SKPhysicsBody(circleOfRadius:bird.size.height/4.0);
bird.physicsBody?.dynamic = true
bird.physicsBody?.allowsRotation = false
self.addChild(bird)
//off screen
//Ground
var groundTexture = SKTexture(imageNamed:"rainbowobstacle")
var sprite = SKSpriteNode(texture: groundTexture)
sprite.setScale(2.0)
sprite.position = CGPointMake(self.size.width/2.0, sprite.size.height/2.0)
self.addChild(sprite)
var ground = SKNode()
ground.position = CGPointMake(0, groundTexture.size().height)
ground.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.frame.size.width, groundTexture.size().height * 2.0))
ground.physicsBody?.dynamic = false
self.addChild(ground)
//Pipes
//create the pipes
pipeUpTexture = SKTexture(imageNamed:"rainbowpipe")
pipeDownTexture = SKTexture(imageNamed:"rainbowpipe")
//move pipes
let distanceToMove = CGFloat(self.frame.size.width + 2.0 * pipeUpTexture.size().width)
let movePipes = SKAction.moveByX(-2500, y: 0.0, duration: NSTimeInterval(0.01 * distanceToMove))
let removePipes = SKAction.removeFromParent()
PipeMoveAndRemove = SKAction.sequence([movePipes, removePipes])
//spwan pipes
let spawn = SKAction.runBlock({() in self.spawnPipes()})
let delay = SKAction.waitForDuration(NSTimeInterval(0.2))
let spawnThenDelay = SKAction.sequence([spawn, delay])
let spawnThenDelayForever = SKAction.repeatActionForever(spawnThenDelay)
self.runAction(spawnThenDelayForever)
let pipeUp = SKSpriteNode(texture:pipeUpTexture)
if (pipeUp.position.x + (pipeUp.size.width / 2) < self.view!.bounds.size.width / 2)
{
score++
}
scoreLabel.hidden = false
}
func spawnPipes(){
let pipePair = SKNode()
pipePair.position = CGPointMake(self.frame.size.width + pipeUpTexture.size().width * 2, 0)
pipePair.zPosition = -10
let height = UInt32(self.frame.size.height / 4)
let y = arc4random() % height + height
let pipeDown = SKSpriteNode(texture:pipeDownTexture)
pipeDown.setScale(2.0)
pipeDown.position = CGPointMake(0.0, CGFloat(y) + pipeDown.size.height + CGFloat(pipeGap))
pipeDown.physicsBody = SKPhysicsBody(rectangleOfSize:pipeDown.size)
pipeDown.physicsBody?.dynamic = false
pipePair.addChild(pipeDown)
let pipeUp = SKSpriteNode(texture:pipeUpTexture)
pipeUp.setScale(2.0)
pipeUp.position = CGPointMake(0.0, CGFloat(y))
pipeUp.physicsBody = SKPhysicsBody(rectangleOfSize: pipeUp.size)
pipeUp.physicsBody?.dynamic = false
pipePair.addChild(pipeUp)
pipePair.runAction(PipeMoveAndRemove)
self.addChild(pipePair)
}
class GameScene: SKScene {
let bird = SKSpriteNode()
var gameOver = false
override init(size: CGSize) {
super.init(size: size)
self.bird.position = CGPoint(x: 0, y: 0)
self.addChild(self.bird)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func endGame(){
// restart the game
let gameScene = GameScene(size: self.size)
self.view!.presentScene(gameScene)
}
override func update(currentTime: NSTimeInterval) {
// our bird doesnt intersect the frame,
// we use gameOver bool so we dont call endGame more than once
if !self.frame.intersects(self.bird.frame) && !self.gameOver{
self.gameOver = true
self.endGame()
}
}
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
bird.physicsBody?.velocity = CGVectorMake(0, 0)
bird.physicsBody?.applyImpulse(CGVectorMake(0, 8))
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
Your label might be covered over by another image. Try setting the SKLabelNode zPosition property to a higher value. Perhaps something like 900.
Try setting the zPosition of the label to one.
scoreLabel.zPosition = 1
This should make the node appear even if another node passes over it.
Here are two other functions that may assist you also:
scoreLabel.color = UIColor.blackColor()//set color to black - you can use any almost color here
scoreLabel.fontSize = 32//changes font size to make label bigger/smaller