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.
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,
- get your image atlas as an
SKTextureAtlas
object - get each texture in the atlas using
textureNamed
and put each of them into an[SKTexture]
- 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.
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 SKNode
s 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
Include an Extension for a Class Only If iOS11 Is Available
What Does <> (Angle Brackets) Do on Class Names in Swift
Load a Collada (Dae) File into Scnnode (Swift - Scenekit)
Sending a Parameter Argument to Function Through Uitapgesturerecognizer Selector
How to Manually Decode an an Array in Swift 4 Codable
How to Use JSON Arrays with Alamofire Parameters
Swiftui: Navigation Bar Title in Reusable Cross-Platform (iOS & MACos) View
How to Add External .Vtt Subtitle File to Avplayerviewcontroller in Tvos
Sum Values of Properties Inside Array of Custom Objects Using Reduce
Add Custom Header to Collection View Swift
Simple Clickable Link in Cocoa and Swift
Lazy Readonly Property in Swift
Cannot Read the Nfc Chip of the Epassport Using iOS13
Understanding Shorthand Closure Syntax for Map Function in Swift
Error: Unable to Spawn Process (Argument List Too Long) in Xcode Build