Sam Sam - 2 months ago 16
Swift Question

Background animation with depth in SpriteKit

I'm building my first game using Swift and SpriteKit and want to add a background. The game takes place in space so I wanted to have stars in the background moving at varying speeds. Currently, I'm going for a 3D look by making the larger stars move across screen faster than the smaller ones. Is there an efficient way to do this rather than making a SKNode subclass like this and adding it as a child at the start of

DidMoveToView
? It seems like this method is pretty intensive but I thought I'd try it before recycling the same image over and over.

class BackGroundAnimation:SKNode{

let theView:SKView
init(aView:SKView){

theView = aView

super.init()

animate()
}


func animate(){


for _ in 1...200{

let randomSize = random(1, max: 3)
var randomPosx = random(1,max: 1000)
randomPosx = randomPosx/1000.0
var randomPosy = random(1,max: 1000)
randomPosy = randomPosy/1000.0

let star:SKSpriteNode = SKSpriteNode(texture:starTexture)
star.setScale(randomSize/60.0)



star.position = CGPoint(x:(theView.scene?.size.width)! * randomPosx,y:(theView.scene?.size.width)! * randomPosy)// (self.scene.size.width)*randomPosx, y:(self.scene.size.height) * randomPosy)

//star.position = CGPoint(x: 200,y: 200)

star.physicsBody = SKPhysicsBody(circleOfRadius: star.size.width/2 )
star.physicsBody?.collisionBitMask = 0
star.physicsBody?.categoryBitMask = 0
star.physicsBody?.contactTestBitMask = 0

star.physicsBody?.linearDamping = 0
star.physicsBody?.velocity = CGVector(dx:1 * randomSize, dy:0)
star.name = "star"

//addChild(star)
self.addChild(star)
self.moveToParent(self.scene!)


}


}




required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}


}

Any help would be great.

Answer

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.

enter image description here

Comments