Background Animation with Depth in Spritekit

Background animation with depth in SpriteKit

As I mentioned in a comment, you can create a beautiful parallax background using particles.

Add this function anywhere in your class.

//Creates a new star field
func starfieldEmitterNode(speed speed: CGFloat, lifetime: CGFloat, scale: CGFloat, birthRate: CGFloat, color: SKColor) -> SKEmitterNode {
let star = SKLabelNode(fontNamed: "Helvetica")
star.fontSize = 80.0
star.text = "✦"
let textureView = SKView()
let texture = textureView.textureFromNode(star)
texture!.filteringMode = .Nearest

let emitterNode = SKEmitterNode()
emitterNode.particleTexture = texture
emitterNode.particleBirthRate = birthRate
emitterNode.particleColor = color
emitterNode.particleLifetime = lifetime
emitterNode.particleSpeed = speed
emitterNode.particleScale = scale
emitterNode.particleColorBlendFactor = 1
emitterNode.position = CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMaxY(frame))
emitterNode.particlePositionRange = CGVector(dx: CGRectGetMaxX(frame), dy: 0)
emitterNode.particleSpeedRange = 16.0

//Rotates the stars
emitterNode.particleAction = SKAction.repeatActionForever(SKAction.sequence([
SKAction.rotateByAngle(CGFloat(-M_PI_4), duration: 1),
SKAction.rotateByAngle(CGFloat(M_PI_4), duration: 1)]))

//Causes the stars to twinkle
let twinkles = 20
let colorSequence = SKKeyframeSequence(capacity: twinkles*2)
let twinkleTime = 1.0 / CGFloat(twinkles)
for i in 0..<twinkles {
colorSequence.addKeyframeValue(SKColor.whiteColor(),time: CGFloat(i) * 2 * twinkleTime / 2)
colorSequence.addKeyframeValue(SKColor.yellowColor(), time: (CGFloat(i) * 2 + 1) * twinkleTime / 2)
}
emitterNode.particleColorSequence = colorSequence

emitterNode.advanceSimulationTime(NSTimeInterval(lifetime))
return emitterNode
}

And then add this function too. This is the function that will create the layers of stars. Just call this function, such as in the didMoveToView.

func createStarLayers() {
//A layer of a star field
let starfieldNode = SKNode()
starfieldNode.name = "starfieldNode"
starfieldNode.addChild(starfieldEmitterNode(speed: -48, lifetime: size.height / 23, scale: 0.2, birthRate: 1, color: SKColor.lightGrayColor()))
addChild(starfieldNode)

//A second layer of stars
var emitterNode = starfieldEmitterNode(speed: -32, lifetime: size.height / 10, scale: 0.14, birthRate: 2, color: SKColor.grayColor())
emitterNode.zPosition = -10
starfieldNode.addChild(emitterNode)

//A third layer
emitterNode = starfieldEmitterNode(speed: -20, lifetime: size.height / 5, scale: 0.1, birthRate: 5, color: SKColor.darkGrayColor())
starfieldNode.addChild(emitterNode)
}

And this is how it looks like.

Sample Image

How to make animated spritekit background?

You just need one node for the background ya know.

Basically, you create each frame of your animated background as images. You can put these images into an image atlas in the asset catalogue.

Now, you can add a single sprite node that covers the whole scene as the "background". You just set the texture of the sprite node to the first frame of your animation.

Then, in didMoveToView or some methods like that,

  1. get your image atlas as an SKTextureAtlas object
  2. get each texture in the atlas using textureNamed and put each of them into an [SKTexture]
  3. Now you have the array, you can do something like this:

-

yourBackgroundSprite.runAction(SKAction.repeatForever(
SKAction.animateWithtextures(theTexturesYouCreatedInStep2, timePerFrame: 30)
))

This link might also help you.

Infinite Background problem with division SpriteKit

I do the test and most likely you should use something like:

activeBackground2.position.y = activeBackground1.size.height + activeBackground1.position.y

instead of

activeBackground2.position.y = -abs(activeBackground1.size.height-activeBackground1.position.y)

I did this example and it works correctly: https://github.com/Maetschl/SpriteKitExamples/tree/master/InfiniteBackground/InfiniteBackground
Feel free to see and use.

Sample Image

Setting a repeated background in an SKScene

I think this will help you, Firstly set the background using the below code:

for index in 0..<2 {
let bg = SKSpriteNode(imageNamed: "Your image name")
bg.position = CGPoint(x: index * Int(bg.size.width), y: 0)
bg.anchorPoint = CGPoint(x: 0, y: 0)
bg.name = "background"

self.addChild(bg)
}

and then use this code to move the background:

self.enumerateChildNodes(withName: "background", using: {(node, stop) -> Void in
if let bg = node as? SKSpriteNode {
bg.position = CGPoint(x: bg.position.x - 3.0, y: bg.position.y)

if bg.position.x <= -bg.size.width {
bg.position = CGPoint(x: bg.position.x + bg.size.width * 2, y: bg.position.y)
}
}
})

SpriteKit animate nodes to specific position

Took a while but here's something:

import SpriteKit

class GameScene: SKScene {

var sprite = SKSpriteNode()

override func didMove(to view: SKView) {

for _ in 1...15 {
spawnsprite()
}
}

func spawnsprite() {

sprite = SKSpriteNode(color: SKColor.yellow, size: CGSize(width: 50, height: 50))
sprite.position = CGPoint(x: (-size.width/2)+50, y: (-size.height/2)+50)
addChild(sprite)

let destination = CGPoint(x: (size.width/2)-50, y: (size.height/2)-50)
let path = createCurvedPath(from: sprite.position, to: destination, varyingBy: 500)

let squareSpeed = CGFloat(arc4random_uniform(600)) + 200
let moveAction = SKAction.follow(path, asOffset: false, orientToPath: false, speed: squareSpeed)
let rotateAction = SKAction.repeatForever(SKAction.rotate(byAngle: 2 * CGFloat.pi, duration: 2))
sprite.run(SKAction.group([moveAction, rotateAction]))
}

func createCurvedPath(from start: CGPoint, to destination: CGPoint, varyingBy offset: UInt32) -> CGMutablePath {
let pathToMove = CGMutablePath()
pathToMove.move(to: start)
let controlPoint = CGPoint(x: CGFloat(arc4random_uniform(offset)) - CGFloat(offset/2),
y: CGFloat(arc4random_uniform(offset)) - CGFloat(offset/2))
pathToMove.addQuadCurve(to: destination, control: controlPoint)
return pathToMove
}

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

There's a lot that can be done to make this nicer (e.g. subclass SKSpriteNode and make the path that the ode follows a property of the sprite itself), but it illustrates the principle.

It could probably also be done with an SKEmitter.

Runnning SpriteKit processes in background threads to improve CPU usage

If this is your first game, you may not want to go crazy with multiple threads unless you really know what you are doing. Be aware that manipulating SKNodes must be done on the same thread that SKView(Private) _update: is run on. This is the same thread that runAction blocks are run on. If you don't you'll crash. In a SpriteKit game I was working on, I was multi-threaded and was not using SKAction or runAction for any updating. I had to eventually add runAction for tree management. I'm currently in the process of re-writing this in OpenGL because of this.

There are certain tasks which make sense to offload, like loading files or even network calls. You should be using GCD for those types of things. Keep in mind that multi-threading does not reduce CPU time as you are imagining.

Odds are your code is doing something bad which is causing you to eat CPU time. Even if you multi-thread, you will end up with the same high CPU but with the problem of a more difficult time debugging. Note I'm not saying its impossible.

Spend the time and figure out really why your CPU usage is high. Based on your other threads, you are not doing a lot.

As an experiment take that spaceship example and run it on your device. Keep adding ships and see what it does to the CPU. Find out how many you have to add before CPU and FPS degrade. Compare that to your game, keeping in mind that the spaceship example is just a simple demo.



Related Topics



Leave a reply



Submit